Completed
Pull Request — master (#82)
by
unknown
01:49
created

UnixTerminal::moveCursorDown()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace PhpSchool\CliMenu\Terminal;
4
5
/**
6
 * @author Michael Woodward <[email protected]>
7
 */
8
class UnixTerminal implements TerminalInterface
9
{
10
    /**
11
     * @var bool
12
     */
13
    private $isTTY;
14
15
    /**
16
     * @var bool
17
     */
18
    private $isCanonical = false;
19
20
    /**
21
     * @var int
22
     */
23
    private $width;
24
25
    /**
26
     * @var int
27
     */
28
    private $height;
29
30
    /**
31
     * @var string
32
     */
33
    private $details;
34
35
    /**
36
     * @var string
37
     */
38
    private $originalConfiguration;
39
40
    /**
41
     * Initialise the terminal from resource
42
     *
43
     */
44
    public function __construct()
45
    {
46
        $this->getOriginalConfiguration();
47
    }
48
49
    /**
50
     * Get the available width of the terminal
51
     */
52
    public function getWidth() : int
53
    {
54
        return $this->width ?: $this->width = (int) exec('tput cols');
55
    }
56
57
    /**
58
     * Get the available height of the terminal
59
     */
60
    public function getHeight() : int
61
    {
62
        return $this->height ?: $this->height = (int) exec('tput lines');
63
    }
64
65
    /**
66
     * Get terminal details
67
     */
68
    public function getDetails() : string
69
    {
70
        if (!$this->details) {
71
            $this->details = function_exists('posix_ttyname')
72
                ? @posix_ttyname(STDOUT)
73
                : "Can't retrieve terminal details";
74
        }
75
76
        return $this->details;
77
    }
78
79
    /**
80
     * Get the original terminal configuration / mode
81
     */
82
    private function getOriginalConfiguration() : string
83
    {
84
        return $this->originalConfiguration ?: $this->originalConfiguration = exec('stty -g');
85
    }
86
87
    /**
88
     * Toggle canonical mode on TTY
89
     */
90
    public function setCanonicalMode(bool $useCanonicalMode = true) : void
91
    {
92
        if ($useCanonicalMode) {
93
            exec('stty -icanon');
94
            $this->isCanonical = true;
95
        } else {
96
            exec('stty ' . $this->getOriginalConfiguration());
97
            $this->isCanonical = false;
98
        }
99
    }
100
101
    /**
102
     * Check if TTY is in canonical mode
103
     * Assumes the terminal was never in canonical mode
104
     */
105
    public function isCanonical() : bool
106
    {
107
        return $this->isCanonical;
108
    }
109
110
    /**
111
     * Test whether terminal is valid TTY
112
     */
113
    public function isTTY() : bool
114
    {
115
        return $this->isTTY ?: $this->isTTY = function_exists('posix_isatty') && @posix_isatty(STDOUT);
116
    }
117
118
    /**
119
     * Test whether terminal supports colour output
120
     *
121
     * @link https://github.com/symfony/Console/blob/master/Output/StreamOutput.php#L95-L102
122
     */
123
    public function supportsColour() : bool
124
    {
125
        if (DIRECTORY_SEPARATOR === '\\') {
126
            return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM');
127
        }
128
129
        return $this->isTTY();
130
    }
131
132
    public function getKeyedInput() : string
133
    {
134
        // TODO: Move to class var?
135
        // TODO: up, down, enter etc in Abstract CONSTs
136
        $map = [
137
            "\033[A" => 'up',
138
            "k"      => 'up',
139
            ""      => 'up', // emacs ^P
140
            "\033[B" => 'down',
141
            "j"      => 'down',
142
            ""      => 'down', //emacs ^N
143
            "\n"     => 'enter',
144
            "\r"     => 'enter',
145
            " "      => 'enter',
146
        ];
147
148
        $input = fread(STDIN, 4);
149
        $this->clearLine();
150
151
        return array_key_exists($input, $map)
152
            ? $map[$input]
153
            : $input;
154
    }
155
156
    /**
157
     * Clear the terminal window
158
     */
159
    public function clear() : void
160
    {
161
        echo "\033[2J";
162
    }
163
164
    /**
165
     * Enable cursor
166
     */
167
    public function enableCursor() : void
168
    {
169
        echo "\033[?25h";
170
    }
171
172
    /**
173
     * Disable cursor
174
     */
175
    public function disableCursor() : void
176
    {
177
        echo "\033[?25l";
178
    }
179
180
    /**
181
     * Move the cursor to the top left of the window
182
     *
183
     * @return void
184
     */
185
    public function moveCursorToTop() : void
186
    {
187
        echo "\033[H";
188
    }
189
190
    /**
191
     * Move the cursor up a specific number of rows
192
     */
193
    public function moveCursorUp(int $numberOfRows) : void
194
    {
195
        echo sprintf("\033[%dA", $numberOfRows);
196
    }
197
198
    /**
199
     * Move the cursor down a specific number of rows
200
     */
201
    public function moveCursorDown(int $numberOfRows) : void
202
    {
203
        echo sprintf("\033[%dB", $numberOfRows);
204
    }
205
206
    /**
207
     * Move the cursor to the start of a specific row
208
     */
209
    public function moveCursorToRow(int $rowNumber) : void
210
    {
211
        echo sprintf("\033[%d;0H", $rowNumber);
212
    }
213
214
    /**
215
     * Move the cursor to the start of a specific column
216
     */
217
    public function moveCursorToColumn(int $column) : void
218
    {
219
        echo sprintf("\033[%dC", $column);
220
    }
221
222
    /**
223
     * Clear the current cursors line
224
     */
225
    public function clearLine() : void
226
    {
227
        echo sprintf("\033[%dD\033[K", $this->getWidth());
228
    }
229
230
    /**
231
     * Clean the whole console without jumping the window
232
     */
233
    public function clean() : void
234
    {
235
        foreach (range(0, $this->getHeight()) as $rowNum) {
236
            $this->moveCursorToRow($rowNum);
237
            $this->clearLine();
238
        }
239
    }
240
}
241