Add ActiveState property to DisplayUnitInfo
This new property can be used if the display is in active (detected) state. Bug: b/277952661 Test: DisplayManagerTest.UpdateDisplayTest, DisplayInfoProviderChromeosTest.GetBasic Change-Id: I5ea2a19ef9a28a9e21b7e61a67442aa8a8a74c06 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4644292 Commit-Queue: Mitsuru Oshima <oshima@chromium.org> Reviewed-by: Ahmed Fakhry <afakhry@chromium.org> Reviewed-by: Stefan Kuhne <skuhne@chromium.org> Cr-Commit-Position: refs/heads/main@{#1164506}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
c030b422a1
commit
9dc9cf99e9
ash/display
chrome/browser/extensions/system_display
chromeos/crosapi/mojom
extensions
tools/typescript/definitions
ui/display
@ -315,6 +315,7 @@ crosapi::mojom::DisplayUnitInfoPtr GetDisplayUnitInfo(
|
||||
info->is_primary = display.id() == primary_id;
|
||||
info->is_internal = display.IsInternal();
|
||||
info->is_enabled = true;
|
||||
info->is_detected = display.detected();
|
||||
info->is_auto_rotation_allowed =
|
||||
Shell::Get()->screen_orientation_controller()->IsAutoRotationAllowed() &&
|
||||
display.IsInternal();
|
||||
|
@ -109,14 +109,14 @@ class DisplayManagerTest : public AshTestBase,
|
||||
|
||||
const vector<display::Display>& changed() const { return changed_; }
|
||||
const vector<display::Display>& added() const { return added_; }
|
||||
uint32_t changed_metrics() const {
|
||||
uint32_t changed_metrics = 0;
|
||||
int32_t changed_metrics() const {
|
||||
int32_t changed_metrics = 0;
|
||||
for (const auto& display_metrics : changed_metrics_) {
|
||||
changed_metrics |= display_metrics.second;
|
||||
}
|
||||
return changed_metrics;
|
||||
}
|
||||
uint32_t changed_metrics(int64_t display_id) const {
|
||||
int32_t changed_metrics(int64_t display_id) const {
|
||||
return changed_metrics_.at(display_id);
|
||||
}
|
||||
|
||||
@ -336,16 +336,34 @@ TEST_F(DisplayManagerTest, UpdateDisplayTest) {
|
||||
const vector<display::ManagedDisplayInfo> empty;
|
||||
display_manager()->OnNativeDisplaysChanged(empty);
|
||||
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
|
||||
// Going to 0 displays doesn't actually change the list and is effectively
|
||||
// ignored.
|
||||
EXPECT_EQ("0 0 0 0 0", GetCountSummary());
|
||||
// Going to 0 displays doesn't actually change the list and but the detected
|
||||
// is set to false.
|
||||
EXPECT_EQ("1 0 0 0 0", GetCountSummary());
|
||||
EXPECT_FALSE(root_window_destroyed());
|
||||
// Display configuration stays the same
|
||||
EXPECT_EQ(gfx::Rect(0, 0, 800, 300),
|
||||
display_manager()->GetDisplayAt(0).bounds());
|
||||
EXPECT_FALSE(display_manager()->GetDisplayAt(0).detected());
|
||||
EXPECT_EQ(changed_metrics(),
|
||||
display::DisplayObserver::DISPLAY_METRIC_DETECTED);
|
||||
reset();
|
||||
|
||||
// Connect to display again
|
||||
// Connect to display again.
|
||||
UpdateDisplay("1+1-800x300");
|
||||
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
|
||||
EXPECT_EQ("1 0 0 1 1", GetCountSummary());
|
||||
EXPECT_FALSE(root_window_destroyed());
|
||||
EXPECT_EQ(gfx::Rect(800, 300), changed()[0].bounds());
|
||||
EXPECT_EQ(gfx::Rect(1, 1, 800, 300),
|
||||
GetDisplayInfo(changed()[0]).bounds_in_native());
|
||||
EXPECT_TRUE(display_manager()->GetDisplayAt(0).detected());
|
||||
EXPECT_EQ(changed_metrics(),
|
||||
display::DisplayObserver::DISPLAY_METRIC_DETECTED);
|
||||
|
||||
// Resumed with different resolution.
|
||||
display_manager()->OnNativeDisplaysChanged(empty);
|
||||
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
|
||||
reset();
|
||||
UpdateDisplay("100+100-500x400");
|
||||
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
|
||||
EXPECT_EQ("1 0 0 1 1", GetCountSummary());
|
||||
@ -353,6 +371,12 @@ TEST_F(DisplayManagerTest, UpdateDisplayTest) {
|
||||
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), changed()[0].bounds());
|
||||
EXPECT_EQ(gfx::Rect(100, 100, 500, 400),
|
||||
GetDisplayInfo(changed()[0]).bounds_in_native());
|
||||
EXPECT_TRUE(display_manager()->GetDisplayAt(0).detected());
|
||||
EXPECT_EQ(changed_metrics(),
|
||||
(display::DisplayObserver::DISPLAY_METRIC_DETECTED |
|
||||
display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
|
||||
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA));
|
||||
|
||||
reset();
|
||||
|
||||
// Go back to zero and wake up with multiple displays.
|
||||
@ -1967,7 +1991,7 @@ TEST_F(DisplayManagerTest, DisplayRemovedOnlyOnceWhenEnteringDockedMode) {
|
||||
|
||||
// There should only be 1 display change, 0 adds, and 1 removal.
|
||||
EXPECT_EQ("1 0 1 1 1", GetCountSummary());
|
||||
const unsigned int expected_changed_metrics =
|
||||
const int expected_changed_metrics =
|
||||
display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
|
||||
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA |
|
||||
display::DisplayObserver::DISPLAY_METRIC_PRIMARY;
|
||||
|
@ -220,6 +220,7 @@ TEST_F(DisplayInfoProviderChromeosTest, GetBasic) {
|
||||
EXPECT_EQ(96, result[0].dpi_y);
|
||||
EXPECT_TRUE(result[0].mirroring_source_id.empty());
|
||||
EXPECT_TRUE(result[0].is_enabled);
|
||||
EXPECT_EQ(api::system_display::ActiveState::kActive, result[0].active_state);
|
||||
|
||||
ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id))
|
||||
<< "Display id must be convertible to integer: " << result[0].id;
|
||||
@ -237,6 +238,39 @@ TEST_F(DisplayInfoProviderChromeosTest, GetBasic) {
|
||||
EXPECT_EQ(96, result[1].dpi_y);
|
||||
EXPECT_TRUE(result[1].mirroring_source_id.empty());
|
||||
EXPECT_TRUE(result[1].is_enabled);
|
||||
EXPECT_EQ(api::system_display::ActiveState::kActive, result[1].active_state);
|
||||
|
||||
// Disconnect all displays.
|
||||
UpdateDisplay("");
|
||||
result = GetAllDisplaysInfo();
|
||||
|
||||
ASSERT_EQ(2u, result.size());
|
||||
|
||||
ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id))
|
||||
<< "Display id must be convertible to integer: " << result[0].id;
|
||||
ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found";
|
||||
EXPECT_TRUE(result[0].is_primary);
|
||||
EXPECT_EQ(api::system_display::ActiveState::kInactive,
|
||||
result[0].active_state);
|
||||
|
||||
ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id))
|
||||
<< "Display id must be convertible to integer: " << result[0].id;
|
||||
ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found";
|
||||
EXPECT_EQ("500,0 400x520", SystemInfoDisplayBoundsToString(result[1].bounds));
|
||||
EXPECT_FALSE(result[1].is_primary);
|
||||
EXPECT_EQ(api::system_display::ActiveState::kInactive,
|
||||
result[1].active_state);
|
||||
|
||||
// Reconnect first display.
|
||||
UpdateDisplay("500x600");
|
||||
result = GetAllDisplaysInfo();
|
||||
ASSERT_EQ(1u, result.size());
|
||||
|
||||
ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id))
|
||||
<< "Display id must be convertible to integer: " << result[0].id;
|
||||
ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found";
|
||||
EXPECT_TRUE(result[0].is_primary);
|
||||
EXPECT_EQ(api::system_display::ActiveState::kActive, result[0].active_state);
|
||||
}
|
||||
|
||||
TEST_F(DisplayInfoProviderChromeosTest, GetWithUnifiedDesktop) {
|
||||
|
@ -202,6 +202,9 @@ system_display::DisplayUnitInfo GetDisplayUnitInfoFromMojo(
|
||||
}
|
||||
info.is_primary = mojo_info.is_primary;
|
||||
info.is_internal = mojo_info.is_internal;
|
||||
info.active_state = mojo_info.is_detected
|
||||
? system_display::ActiveState::kActive
|
||||
: system_display::ActiveState::kInactive;
|
||||
info.is_enabled = mojo_info.is_enabled;
|
||||
info.is_auto_rotation_allowed = mojo_info.is_auto_rotation_allowed;
|
||||
info.dpi_x = mojo_info.dpi_x;
|
||||
|
@ -8,6 +8,7 @@ import "ui/gfx/geometry/mojom/geometry.mojom";
|
||||
import "ui/display/mojom/display.mojom";
|
||||
|
||||
// This API is used for communication between Settings WebUI and Ash C++ code.
|
||||
// Next MinVersion: 2
|
||||
|
||||
// All points, bounds, and insets are in display pixels unless otherwise
|
||||
// sepcified.
|
||||
@ -259,6 +260,8 @@ struct DisplayUnitInfo {
|
||||
double display_zoom_factor@17;
|
||||
// The list of allowed zoom factor values for the display.
|
||||
array<double> available_display_zoom_factors@18;
|
||||
// True if the display was detected by the system.
|
||||
[MinVersion=1] bool is_detected@19;
|
||||
};
|
||||
|
||||
// Properties for configuring an individual display, used in
|
||||
|
@ -79,6 +79,9 @@ api::system_display::DisplayUnitInfo DisplayInfoProvider::CreateDisplayUnitInfo(
|
||||
unit.id = base::NumberToString(display.id());
|
||||
unit.is_primary = (display.id() == primary_display_id);
|
||||
unit.is_internal = display.IsInternal();
|
||||
unit.active_state = display.detected()
|
||||
? api::system_display::ActiveState::kActive
|
||||
: api::system_display::ActiveState::kInactive;
|
||||
unit.is_enabled = true;
|
||||
unit.is_unified = false;
|
||||
unit.rotation = RotationToDegrees(display.rotation());
|
||||
@ -134,7 +137,6 @@ void DisplayInfoProvider::GetAllDisplaysInfo(
|
||||
provided_screen_ ? provided_screen_.get() : display::Screen::GetScreen();
|
||||
int64_t primary_id = screen->GetPrimaryDisplay().id();
|
||||
std::vector<display::Display> displays = screen->GetAllDisplays();
|
||||
DisplayUnitInfoList all_displays;
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&DisplayInfoProvider::GetAllDisplaysInfoList,
|
||||
|
@ -130,6 +130,14 @@ namespace system.display {
|
||||
long yearOfManufacture;
|
||||
};
|
||||
|
||||
// An enum to tell if the display is detected and used by the
|
||||
// system. The display is considered 'inactive', if it is not
|
||||
// detected by the system (maybe disconnected, or considered
|
||||
// disconnected due to sleep mode, etc). This state is used to keep
|
||||
// existing display when the all displays are disconnected, for
|
||||
// example.
|
||||
enum ActiveState { active, inactive };
|
||||
|
||||
dictionary DisplayUnitInfo {
|
||||
// The unique identifier of the display.
|
||||
DOMString id;
|
||||
@ -160,6 +168,9 @@ namespace system.display {
|
||||
// True if this display is enabled.
|
||||
boolean isEnabled;
|
||||
|
||||
// Active if the display is detected and used by the system.
|
||||
ActiveState activeState;
|
||||
|
||||
// True for all displays when in unified desktop mode. See documentation
|
||||
// for $(ref:enableUnifiedDesktop).
|
||||
boolean isUnified;
|
||||
|
@ -57,6 +57,14 @@ declare global {
|
||||
LEFT = 'left',
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.chrome.com/extensions/system.display#type-ActiveState
|
||||
*/
|
||||
export enum ActiveState {
|
||||
ACTIVE = 'active',
|
||||
INACTIVE = 'inactive',
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.chrome.com/extensions/system.display#type-DisplayLayout
|
||||
*/
|
||||
@ -87,6 +95,7 @@ declare global {
|
||||
mirroringDestinationIds: string[];
|
||||
isPrimary: boolean;
|
||||
isInternal: boolean;
|
||||
activeState: ActiveState;
|
||||
isEnabled: boolean;
|
||||
isUnified: boolean;
|
||||
isAutoRotationAllowed?: boolean;
|
||||
|
@ -306,11 +306,12 @@ gfx::Size Display::GetSizeInPixel() const {
|
||||
std::string Display::ToString() const {
|
||||
return base::StringPrintf(
|
||||
"Display[%lld] bounds=[%s], workarea=[%s], scale=%g, rotation=%s, "
|
||||
"panel_rotation=%s %s.",
|
||||
"panel_rotation=%s %s %s",
|
||||
static_cast<long long int>(id_), bounds_.ToString().c_str(),
|
||||
work_area_.ToString().c_str(), device_scale_factor_,
|
||||
ToRotationString(rotation_), ToRotationString(panel_rotation()),
|
||||
IsInternal() ? "internal" : "external");
|
||||
IsInternal() ? "internal" : "external",
|
||||
detected() ? "detected" : "not-detected");
|
||||
}
|
||||
|
||||
bool Display::IsInternal() const {
|
||||
@ -326,7 +327,7 @@ int64_t Display::InternalDisplayId() {
|
||||
|
||||
bool Display::operator==(const Display& rhs) const {
|
||||
return id_ == rhs.id_ && bounds_ == rhs.bounds_ &&
|
||||
size_in_pixels_ == rhs.size_in_pixels_ &&
|
||||
size_in_pixels_ == rhs.size_in_pixels_ && detected_ == rhs.detected_ &&
|
||||
work_area_ == rhs.work_area_ &&
|
||||
device_scale_factor_ == rhs.device_scale_factor_ &&
|
||||
rotation_ == rhs.rotation_ && touch_support_ == rhs.touch_support_ &&
|
||||
|
@ -203,6 +203,16 @@ class DISPLAY_EXPORT Display final {
|
||||
// True if the display corresponds to internal panel.
|
||||
bool IsInternal() const;
|
||||
|
||||
// Returns true if the display is detected by the system. A display can
|
||||
// stay 'active' when all displays are disconnected from SW point of view,
|
||||
// because this can happen when the display went to sleep mode, or the
|
||||
// device went to sleep mode, and in that case, we do not want to change
|
||||
// the display configuration (so that it starts in the same state when
|
||||
// resumed). Use this if you want to check if the display is detected by the
|
||||
// system.
|
||||
bool detected() const { return detected_; }
|
||||
void set_detected(bool detected) { detected_ = detected; }
|
||||
|
||||
// [Deprecated] Use `display::GetInternalDisplayIds()`.
|
||||
// Gets an id of display corresponding to internal panel.
|
||||
static int64_t InternalDisplayId();
|
||||
@ -294,6 +304,7 @@ class DISPLAY_EXPORT Display final {
|
||||
int color_depth_;
|
||||
int depth_per_component_;
|
||||
bool is_monochrome_ = false;
|
||||
bool detected_ = true;
|
||||
int display_frequency_ = 0;
|
||||
std::string label_;
|
||||
uint32_t audio_formats_ = 0;
|
||||
|
@ -31,6 +31,7 @@ class DISPLAY_EXPORT DisplayObserver : public base::CheckedObserver {
|
||||
DISPLAY_METRIC_INTERLACED = 1 << 8,
|
||||
DISPLAY_METRIC_LABEL = 1 << 9,
|
||||
DISPLAY_METRIC_VRR = 1 << 10,
|
||||
DISPLAY_METRIC_DETECTED = 1 << 11,
|
||||
};
|
||||
|
||||
// This may be called before other methods to signal changes are about to
|
||||
|
@ -750,10 +750,12 @@ void DisplayManager::OnNativeDisplaysChanged(
|
||||
DisplayInfoList init_displays;
|
||||
init_displays.push_back(
|
||||
ManagedDisplayInfo::CreateFromSpec(std::string()));
|
||||
init_displays[0].set_detected(false);
|
||||
MaybeInitInternalDisplay(&init_displays[0]);
|
||||
OnNativeDisplaysChanged(init_displays);
|
||||
} else {
|
||||
// Otherwise don't update the displays when all displays are disconnected.
|
||||
// Otherwise just update the displays' detected state when all displays
|
||||
// are disconnected.
|
||||
// This happens when:
|
||||
// - the device is idle and powerd requested to turn off all displays.
|
||||
// - the device is suspended. (kernel turns off all displays)
|
||||
@ -763,6 +765,17 @@ void DisplayManager::OnNativeDisplaysChanged(
|
||||
// disconnected.
|
||||
// The display will be updated when one of displays is turned on, and the
|
||||
// display list will be updated correctly.
|
||||
|
||||
for (auto& display : active_display_list_) {
|
||||
if (display.detected()) {
|
||||
ManagedDisplayInfo info = GetDisplayInfo(display.id());
|
||||
info.set_detected(false);
|
||||
display.set_detected(false);
|
||||
InsertAndUpdateDisplayInfo(info);
|
||||
NotifyMetricsChanged(display,
|
||||
DisplayObserver::DISPLAY_METRIC_DETECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -999,6 +1012,9 @@ void DisplayManager::UpdateDisplaysWith(
|
||||
new_display_info.vsync_rate_min()) {
|
||||
metrics |= DisplayObserver::DISPLAY_METRIC_VRR;
|
||||
}
|
||||
if (current_display_info.detected() != new_display_info.detected()) {
|
||||
metrics |= DisplayObserver::DISPLAY_METRIC_DETECTED;
|
||||
}
|
||||
|
||||
if (metrics != DisplayObserver::DISPLAY_METRIC_NONE) {
|
||||
display_changes.insert(
|
||||
@ -1097,13 +1113,17 @@ void DisplayManager::UpdateDisplaysWith(
|
||||
for (auto iter = display_changes.begin(); iter != display_changes.end();
|
||||
++iter) {
|
||||
uint32_t metrics = iter->second;
|
||||
const Display& updated_display = active_display_list_[iter->first];
|
||||
Display& updated_display = active_display_list_[iter->first];
|
||||
|
||||
if (notify_primary_change &&
|
||||
updated_display.id() == screen_->GetPrimaryDisplay().id()) {
|
||||
metrics |= DisplayObserver::DISPLAY_METRIC_PRIMARY;
|
||||
notify_primary_change = false;
|
||||
}
|
||||
if (!updated_display.detected()) {
|
||||
updated_display.set_detected(true);
|
||||
metrics |= DisplayObserver::DISPLAY_METRIC_DETECTED;
|
||||
}
|
||||
NotifyMetricsChanged(updated_display, metrics);
|
||||
}
|
||||
|
||||
@ -2093,6 +2113,7 @@ Display DisplayManager::CreateDisplayFromDisplayInfoById(int64_t id) {
|
||||
new_display.set_label(display_info.name());
|
||||
new_display.SetDRMFormatsAndModifiers(
|
||||
display_info.GetDRMFormatsAndModifiers());
|
||||
new_display.set_detected(display_info.detected());
|
||||
|
||||
constexpr uint32_t kNormalBitDepthNumBitsPerChannel = 8u;
|
||||
if (display_info.bits_per_channel() > kNormalBitDepthNumBitsPerChannel) {
|
||||
|
@ -470,6 +470,7 @@ void ManagedDisplayInfo::Copy(const ManagedDisplayInfo& native_info) {
|
||||
drm_formats_and_modifiers_ = native_info.drm_formats_and_modifiers_;
|
||||
variable_refresh_rate_state_ = native_info.variable_refresh_rate_state_;
|
||||
vsync_rate_min_ = native_info.vsync_rate_min_;
|
||||
detected_ = native_info.detected_;
|
||||
|
||||
// Rotation, color_profile and overscan are given by preference,
|
||||
// or unit tests. Don't copy if this native_info came from
|
||||
@ -577,7 +578,7 @@ std::string ManagedDisplayInfo::ToString() const {
|
||||
std::string result = base::StringPrintf(
|
||||
"ManagedDisplayInfo[%lld] native bounds=%s, size=%s, device-scale=%g, "
|
||||
"display-zoom=%g, overscan=%s, rotation=%d, touchscreen=%s, "
|
||||
"panel_corners_radii=%s, panel_orientation=%s",
|
||||
"panel_corners_radii=%s, panel_orientation=%s, detected=%s",
|
||||
static_cast<long long int>(id_), bounds_in_native_.ToString().c_str(),
|
||||
size_in_pixel_.ToString().c_str(), device_scale_factor_, zoom_factor_,
|
||||
overscan_insets_in_dip_.ToString().c_str(), rotation_degree,
|
||||
@ -585,7 +586,8 @@ std::string ManagedDisplayInfo::ToString() const {
|
||||
: touch_support_ == Display::TouchSupport::UNAVAILABLE ? "no"
|
||||
: "unknown",
|
||||
panel_corners_radii_.ToString().c_str(),
|
||||
PanelOrientationToString(panel_orientation_).c_str());
|
||||
PanelOrientationToString(panel_orientation_).c_str(),
|
||||
detected_ ? "true" : "false");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -226,6 +226,9 @@ class DISPLAY_MANAGER_EXPORT ManagedDisplayInfo {
|
||||
return active_rotation_source_;
|
||||
}
|
||||
|
||||
bool detected() const { return detected_; }
|
||||
void set_detected(bool detected) { detected_ = detected; }
|
||||
|
||||
// Returns the rotation set by a given |source|.
|
||||
Display::Rotation GetRotation(Display::RotationSource source) const;
|
||||
|
||||
@ -427,6 +430,11 @@ class DISPLAY_MANAGER_EXPORT ManagedDisplayInfo {
|
||||
// to distinguish the empty overscan insets from native display info.
|
||||
bool clear_overscan_insets_;
|
||||
|
||||
// True if the display is detected by the system. The system will keep at
|
||||
// least one display available even if all displays are disconnected, and this
|
||||
// field is set to false in such a case.
|
||||
bool detected_ = true;
|
||||
|
||||
// The list of modes supported by this display.
|
||||
ManagedDisplayModeList display_modes_;
|
||||
|
||||
|
@ -161,14 +161,15 @@ TEST_F(DisplayInfoTest, TestToStringFormat) {
|
||||
"device-scale=1, display-zoom=1, overscan=x:0,0 y:0,0, rotation=0, "
|
||||
"touchscreen=unknown, "
|
||||
"panel_corners_radii=0.000000,0.000000,0.000000,0.000000, "
|
||||
"panel_orientation=Normal");
|
||||
"panel_orientation=Normal, detected=true");
|
||||
|
||||
EXPECT_EQ(info.ToFullString(),
|
||||
"ManagedDisplayInfo[10] native bounds=0,0 200x100, size=200x100, "
|
||||
"device-scale=1, display-zoom=1, overscan=x:0,0 y:0,0, rotation=0, "
|
||||
"touchscreen=unknown, "
|
||||
"panel_corners_radii=0.000000,0.000000,0.000000,0.000000, "
|
||||
"panel_orientation=Normal, display_modes==(200x100@60P(N) 1)");
|
||||
"panel_orientation=Normal, detected=true, "
|
||||
"display_modes==(200x100@60P(N) 1)");
|
||||
}
|
||||
|
||||
} // namespace display
|
||||
|
Reference in New Issue
Block a user