Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/**
4
 * Hoa
5
 *
6
 *
7
 * @license
8
 *
9
 * New BSD License
10
 *
11
 * Copyright © 2007-2017, Hoa community. All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without
14
 * modification, are permitted provided that the following conditions are met:
15
 *     * Redistributions of source code must retain the above copyright
16
 *       notice, this list of conditions and the following disclaimer.
17
 *     * Redistributions in binary form must reproduce the above copyright
18
 *       notice, this list of conditions and the following disclaimer in the
19
 *       documentation and/or other materials provided with the distribution.
20
 *     * Neither the name of the Hoa nor the names of its contributors may be
21
 *       used to endorse or promote products derived from this software without
22
 *       specific prior written permission.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 * POSSIBILITY OF SUCH DAMAGE.
35
 */
36
 
37
namespace Psy\Readline\Hoa;
38
 
39
/**
40
 * Allow to manipulate the window.
41
 *
42
 * We can listen the event channel hoa://Event/Console/Window:resize to detect
43
 * if the window has been resized. Please, see the constructor documentation to
44
 * get more informations.
45
 */
46
class ConsoleWindow implements EventSource
47
{
48
    /**
49
     * Singleton (only for events).
50
     */
51
    private static $_instance = null;
52
 
53
    /**
54
     * Set the event channel.
55
     * We need to declare(ticks = 1) in the main script to ensure that the event
56
     * is fired. Also, we need the pcntl_signal() function enabled.
57
     */
58
    private function __construct()
59
    {
60
        Event::register(
61
            'hoa://Event/Console/Window:resize',
62
            $this
63
        );
64
 
65
        return;
66
    }
67
 
68
    /**
69
     * Singleton.
70
     */
71
    public static function getInstance(): self
72
    {
73
        if (null === static::$_instance) {
74
            static::$_instance = new self();
75
        }
76
 
77
        return static::$_instance;
78
    }
79
 
80
    /**
81
     * Set size to X lines and Y columns.
82
     */
83
    public static function setSize(int $x, int $y)
84
    {
85
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
86
            return;
87
        }
88
 
89
        Console::getOutput()->writeAll("\033[8;".$y.';'.$x.'t');
90
 
91
        return;
92
    }
93
 
94
    /**
95
     * Get current size (x and y) of the window.
96
     */
97
    public static function getSize(): array
98
    {
99
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
100
            $modecon = \explode("\n", \ltrim(ConsoleProcessus::execute('mode con')));
101
 
102
            $_y = \trim($modecon[2]);
103
            \preg_match('#[^:]+:\s*([0-9]+)#', $_y, $matches);
104
            $y = (int) $matches[1];
105
 
106
            $_x = \trim($modecon[3]);
107
            \preg_match('#[^:]+:\s*([0-9]+)#', $_x, $matches);
108
            $x = (int) $matches[1];
109
 
110
            return [
111
                'x' => $x,
112
                'y' => $y,
113
            ];
114
        }
115
 
116
        $term = '';
117
 
118
        if (isset($_SERVER['TERM'])) {
119
            $term = 'TERM="'.$_SERVER['TERM'].'" ';
120
        }
121
 
122
        $command = $term.'tput cols && '.$term.'tput lines';
123
        $tput = ConsoleProcessus::execute($command, false);
124
 
125
        if (!empty($tput)) {
126
            list($x, $y) = \explode("\n", $tput);
127
 
128
            return [
129
                'x' => (int) $x,
130
                'y' => (int) $y,
131
            ];
132
        }
133
 
134
        // DECSLPP.
135
        Console::getOutput()->writeAll("\033[18t");
136
 
137
        $input = Console::getInput();
138
 
139
        // Read \033[8;y;xt.
140
        $input->read(4); // skip \033, [, 8 and ;.
141
 
142
        $x = null;
143
        $y = null;
144
        $handle = &$y;
145
 
146
        while (true) {
147
            $char = $input->readCharacter();
148
 
149
            switch ($char) {
150
                case ';':
151
                    $handle = &$x;
152
 
153
                    break;
154
 
155
                case 't':
156
                    break 2;
157
 
158
                default:
159
                    if (false === \ctype_digit($char)) {
160
                        break 2;
161
                    }
162
 
163
                    $handle .= $char;
164
            }
165
        }
166
 
167
        if (null === $x || null === $y) {
168
            return [
169
                'x' => 0,
170
                'y' => 0,
171
            ];
172
        }
173
 
174
        return [
175
            'x' => (int) $x,
176
            'y' => (int) $y,
177
        ];
178
    }
179
 
180
    /**
181
     * Move to X and Y (in pixels).
182
     */
183
    public static function moveTo(int $x, int $y)
184
    {
185
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
186
            return;
187
        }
188
 
189
        // DECSLPP.
190
        Console::getOutput()->writeAll("\033[3;".$x.';'.$y.'t');
191
 
192
        return;
193
    }
194
 
195
    /**
196
     * Get current position (x and y) of the window (in pixels).
197
     */
198
    public static function getPosition(): array
199
    {
200
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
201
            return ['x' => 0, 'y' => 0];
202
        }
203
 
204
        // DECSLPP.
205
        Console::getOutput()->writeAll("\033[13t");
206
 
207
        $input = Console::getInput();
208
 
209
        // Read \033[3;x;yt.
210
        $input->read(4); // skip \033, [, 3 and ;.
211
 
212
        $x = null;
213
        $y = null;
214
        $handle = &$x;
215
 
216
        while (true) {
217
            $char = $input->readCharacter();
218
 
219
            switch ($char) {
220
                case ';':
221
                    $handle = &$y;
222
 
223
                    break;
224
 
225
                case 't':
226
                    break 2;
227
 
228
                default:
229
                    $handle .= $char;
230
            }
231
        }
232
 
233
        return [
234
            'x' => (int) $x,
235
            'y' => (int) $y,
236
        ];
237
    }
238
 
239
    /**
240
     * Scroll whole page.
241
     * Directions can be:
242
     *     • u, up,    ↑ : scroll whole page up;
243
     *     • d, down,  ↓ : scroll whole page down.
244
     * Directions can be concatenated by a single space.
245
     */
246
    public static function scroll(string $directions, int $repeat = 1)
247
    {
248
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
249
            return;
250
        }
251
 
252
        if (1 > $repeat) {
253
            return;
254
        } elseif (1 === $repeat) {
255
            $handle = \explode(' ', $directions);
256
        } else {
257
            $handle = \explode(' ', $directions, 1);
258
        }
259
 
260
        $tput = Console::getTput();
261
        $count = ['up' => 0, 'down' => 0];
262
 
263
        foreach ($handle as $direction) {
264
            switch ($direction) {
265
                case 'u':
266
                case 'up':
267
                case '↑':
268
                    ++$count['up'];
269
 
270
                    break;
271
 
272
                case 'd':
273
                case 'down':
274
                case '↓':
275
                    ++$count['down'];
276
 
277
                    break;
278
            }
279
        }
280
 
281
        $output = Console::getOutput();
282
 
283
        if (0 < $count['up']) {
284
            $output->writeAll(
285
                \str_replace(
286
                    '%p1%d',
287
                    $count['up'] * $repeat,
288
                    $tput->get('parm_index')
289
                )
290
            );
291
        }
292
 
293
        if (0 < $count['down']) {
294
            $output->writeAll(
295
                \str_replace(
296
                    '%p1%d',
297
                    $count['down'] * $repeat,
298
                    $tput->get('parm_rindex')
299
                )
300
            );
301
        }
302
 
303
        return;
304
    }
305
 
306
    /**
307
     * Minimize the window.
308
     */
309
    public static function minimize()
310
    {
311
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
312
            return;
313
        }
314
 
315
        // DECSLPP.
316
        Console::getOutput()->writeAll("\033[2t");
317
 
318
        return;
319
    }
320
 
321
    /**
322
     * Restore the window (de-minimize).
323
     */
324
    public static function restore()
325
    {
326
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
327
            return;
328
        }
329
 
330
        Console::getOutput()->writeAll("\033[1t");
331
 
332
        return;
333
    }
334
 
335
    /**
336
     * Raise the window to the front of the stacking order.
337
     */
338
    public static function raise()
339
    {
340
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
341
            return;
342
        }
343
 
344
        Console::getOutput()->writeAll("\033[5t");
345
 
346
        return;
347
    }
348
 
349
    /**
350
     * Lower the window to the bottom of the stacking order.
351
     */
352
    public static function lower()
353
    {
354
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
355
            return;
356
        }
357
 
358
        Console::getOutput()->writeAll("\033[6t");
359
 
360
        return;
361
    }
362
 
363
    /**
364
     * Set title.
365
     */
366
    public static function setTitle(string $title)
367
    {
368
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
369
            return;
370
        }
371
 
372
        // DECSLPP.
373
        Console::getOutput()->writeAll("\033]0;".$title."\033\\");
374
 
375
        return;
376
    }
377
 
378
    /**
379
     * Get title.
380
     */
381
    public static function getTitle()
382
    {
383
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
384
            return null;
385
        }
386
 
387
        // DECSLPP.
388
        Console::getOutput()->writeAll("\033[21t");
389
 
390
        $input = Console::getInput();
391
        $read = [$input->getStream()->getStream()];
392
        $write = [];
393
        $except = [];
394
        $out = null;
395
 
396
        if (0 === \stream_select($read, $write, $except, 0, 50000)) {
397
            return $out;
398
        }
399
 
400
        // Read \033]l<title>\033\
401
        $input->read(3); // skip \033, ] and l.
402
 
403
        while (true) {
404
            $char = $input->readCharacter();
405
 
406
            if ("\033" === $char) {
407
                $chaar = $input->readCharacter();
408
 
409
                if ('\\' === $chaar) {
410
                    break;
411
                }
412
 
413
                $char .= $chaar;
414
            }
415
 
416
            $out .= $char;
417
        }
418
 
419
        return $out;
420
    }
421
 
422
    /**
423
     * Get label.
424
     */
425
    public static function getLabel()
426
    {
427
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
428
            return null;
429
        }
430
 
431
        // DECSLPP.
432
        Console::getOutput()->writeAll("\033[20t");
433
 
434
        $input = Console::getInput();
435
        $read = [$input->getStream()->getStream()];
436
        $write = [];
437
        $except = [];
438
        $out = null;
439
 
440
        if (0 === \stream_select($read, $write, $except, 0, 50000)) {
441
            return $out;
442
        }
443
 
444
        // Read \033]L<label>\033\
445
        $input->read(3); // skip \033, ] and L.
446
 
447
        while (true) {
448
            $char = $input->readCharacter();
449
 
450
            if ("\033" === $char) {
451
                $chaar = $input->readCharacter();
452
 
453
                if ('\\' === $chaar) {
454
                    break;
455
                }
456
 
457
                $char .= $chaar;
458
            }
459
 
460
            $out .= $char;
461
        }
462
 
463
        return $out;
464
    }
465
 
466
    /**
467
     * Refresh the window.
468
     */
469
    public static function refresh()
470
    {
471
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
472
            return;
473
        }
474
 
475
        // DECSLPP.
476
        Console::getOutput()->writeAll("\033[7t");
477
 
478
        return;
479
    }
480
 
481
    /**
482
     * Set clipboard value.
483
     */
484
    public static function copy(string $data)
485
    {
486
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
487
            return;
488
        }
489
 
490
        $out = "\033]52;;".\base64_encode($data)."\033\\";
491
        $output = Console::getOutput();
492
        $considerMultiplexer = $output->considerMultiplexer(true);
493
 
494
        $output->writeAll($out);
495
        $output->considerMultiplexer($considerMultiplexer);
496
 
497
        return;
498
    }
499
}
500
 
501
/*
502
 * Advanced interaction.
503
 */
504
Console::advancedInteraction();
505
 
506
/*
507
 * Event.
508
 */
509
if (\function_exists('pcntl_signal')) {
510
    ConsoleWindow::getInstance();
511
    \pcntl_signal(
512
        \SIGWINCH,
513
        function () {
514
            static $_window = null;
515
 
516
            if (null === $_window) {
517
                $_window = ConsoleWindow::getInstance();
518
            }
519
 
520
            Event::notify(
521
                'hoa://Event/Console/Window:resize',
522
                $_window,
523
                new EventBucket([
524
                    'size' => ConsoleWindow::getSize(),
525
                ])
526
            );
527
        }
528
    );
529
}