1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    fidl_fuchsia_hardware_display::ClientCompositionOp,
    fidl_fuchsia_hardware_display_types::{ClientCompositionOpcode, ConfigResult},
    fuchsia_zircon as zx,
    futures::channel::mpsc,
    thiserror::Error,
};

use crate::{
    controller::VsyncEvent,
    types::{DisplayId, LayerId},
};

/// Library error type.
#[derive(Error, Debug)]
pub enum Error {
    /// Error encountered while connecting to a display-coordinator device via devfs.
    #[error("could not find a display-coordinator device")]
    DeviceNotFound,

    /// No displays were reported by the display driver when expected.
    #[error("device did not enumerate initial displays")]
    NoDisplays,

    /// A request handling task (such as one that owns a FIDL event stream that can only be
    /// started once) was already been initiated before.
    #[error("a singleton task was already initiated")]
    AlreadyRequested,

    /// Error while allocating shared sysmem buffers.
    #[error("sysmem buffer collection allocation failed, or invalid response from sysmem")]
    BuffersNotAllocated,

    /// Error while establishing a connection to sysmem.
    #[error("error while setting up a sysmem connection")]
    SysmemConnection,

    /// Ran out of free client-assigned identifiers.
    #[error("ran out of identifiers")]
    IdsExhausted,

    /// Path to the device is invalid (e.g. containing invalid
    /// characters).
    #[error("invalid device path")]
    DevicePathInvalid,

    /// Wrapper for errors from FIDL bindings.
    #[error("FIDL error: {0}")]
    FidlError(#[from] fidl::Error),

    /// Wrapper for system file I/O errors.
    #[error("OS I/O error: {0}")]
    IoError(#[from] std::io::Error),

    /// Wrapper for errors from zircon syscalls.
    #[error("zircon error: {0}")]
    ZxError(#[from] zx::Status),

    /// Wrapper for errors from FIDL device connections.
    #[error("Device connection error: {0}")]
    DeviceConnectionError(anyhow::Error),

    /// Wrapper for errors from fuchsia-fs.
    #[error("filesystem error: {0}")]
    FsError(#[from] fuchsia_fs::node::OpenError),

    /// Wrapper for errors from fuchsia-fs watcher creation.
    #[error("failed to create directory watcher: {0}")]
    WatcherCreateError(#[from] fuchsia_fs::directory::WatcherCreateError),

    /// Wrapper for errors from the fuchsia-fs watcher stream.
    #[error("directory watcher stream produced error: {0}")]
    WatcherStreamError(#[from] fuchsia_fs::directory::WatcherStreamError),

    /// Error that occurred while notifying vsync event listeners over an in-process async channel.
    #[error("failed to notify vsync: {0}")]
    CouldNotSendVsyncEvent(#[from] mpsc::TrySendError<VsyncEvent>),

    /// UTF-8 validation error.
    #[error("invalid UTF-8 string")]
    InvalidUtf8(#[from] std::str::Utf8Error),
}

/// Library Result type alias.
pub type Result<T> = std::result::Result<T, Error>;

/// An error generated by `fuchsia.hardware.display.Controller.CheckConfig`.
#[derive(Debug, Error)]
pub enum ConfigError {
    /// Failure due to an invalid configuration.
    #[error("invalid configuration - error_code: {error_code:#?}, actions: {actions:#?}")]
    Invalid {
        /// The reason for the failure.
        error_code: ConfigResult,

        /// Suggested actions that the client can take to resolve the failure.
        actions: Vec<ClientCompositionAction>,
    },

    /// Failure due to a FIDL transport error.
    #[error("FIDL channel error")]
    Fidl(#[from] fidl::Error),
}

/// Represents a suggested client composition action generated by the driver.
#[derive(Debug, Clone)]
pub struct ClientCompositionAction {
    /// The ID of the display that concerns the action.
    pub display_id: DisplayId,

    /// The ID of the layer that the action should be taken on.
    pub layer_id: LayerId,

    /// Description of the action.
    pub opcode: ClientCompositionOpcode,
}

impl ConfigError {
    /// Create an `Invalid` configuration error variant from FIDL output.
    pub fn invalid(error_code: ConfigResult, actions: Vec<ClientCompositionOp>) -> ConfigError {
        ConfigError::Invalid {
            error_code,
            actions: actions.into_iter().map(ClientCompositionAction::from).collect(),
        }
    }
}

impl From<ClientCompositionOp> for ClientCompositionAction {
    fn from(src: ClientCompositionOp) -> Self {
        Self {
            display_id: src.display_id.into(),
            layer_id: src.layer_id.into(),
            opcode: src.opcode,
        }
    }
}