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
 * Class \Hoa\Console\Cursor.
41
 *
42
 * Allow to manipulate the cursor.
43
 */
44
class ConsoleCursor
45
{
46
    /**
47
     * Move the cursor.
48
     * Steps can be:
49
     *     • u, up,    ↑ : move to the previous line;
50
     *     • U, UP       : move to the first line;
51
     *     • r, right, → : move to the next column;
52
     *     • R, RIGHT    : move to the last column;
53
     *     • d, down,  ↓ : move to the next line;
54
     *     • D, DOWN     : move to the last line;
55
     *     • l, left,  ← : move to the previous column;
56
     *     • L, LEFT     : move to the first column.
57
     * Steps can be concatened by a single space if $repeat is equal to 1.
58
     */
59
    public static function move(string $steps, int $repeat = 1)
60
    {
61
        if (1 > $repeat) {
62
            return;
63
        } elseif (1 === $repeat) {
64
            $handle = \explode(' ', $steps);
65
        } else {
66
            $handle = \explode(' ', $steps, 1);
67
        }
68
 
69
        $tput = Console::getTput();
70
        $output = Console::getOutput();
71
 
72
        foreach ($handle as $step) {
73
            switch ($step) {
74
                case 'u':
75
                case 'up':
76
                case '↑':
77
                    $output->writeAll(
78
                        \str_replace(
79
                            '%p1%d',
80
                            $repeat,
81
                            $tput->get('parm_up_cursor')
82
                        )
83
                    );
84
 
85
                    break;
86
 
87
                case 'U':
88
                case 'UP':
89
                    static::moveTo(null, 1);
90
 
91
                    break;
92
 
93
                case 'r':
94
                case 'right':
95
                case '→':
96
                    $output->writeAll(
97
                        \str_replace(
98
                            '%p1%d',
99
                            $repeat,
100
                            $tput->get('parm_right_cursor')
101
                        )
102
                    );
103
 
104
                    break;
105
 
106
                case 'R':
107
                case 'RIGHT':
108
                    static::moveTo(9999);
109
 
110
                    break;
111
 
112
                case 'd':
113
                case 'down':
114
                case '↓':
115
                    $output->writeAll(
116
                        \str_replace(
117
                            '%p1%d',
118
                            $repeat,
119
                            $tput->get('parm_down_cursor')
120
                        )
121
                    );
122
 
123
                    break;
124
 
125
                case 'D':
126
                case 'DOWN':
127
                    static::moveTo(null, 9999);
128
 
129
                    break;
130
 
131
                case 'l':
132
                case 'left':
133
                case '←':
134
                    $output->writeAll(
135
                        \str_replace(
136
                            '%p1%d',
137
                            $repeat,
138
                            $tput->get('parm_left_cursor')
139
                        )
140
                    );
141
 
142
                    break;
143
 
144
                case 'L':
145
                case 'LEFT':
146
                    static::moveTo(1);
147
 
148
                    break;
149
            }
150
        }
151
    }
152
 
153
    /**
154
     * Move to the line X and the column Y.
155
     * If null, use the current coordinate.
156
     */
157
    public static function moveTo(int $x = null, int $y = null)
158
    {
159
        if (null === $x || null === $y) {
160
            $position = static::getPosition();
161
 
162
            if (null === $x) {
163
                $x = $position['x'];
164
            }
165
 
166
            if (null === $y) {
167
                $y = $position['y'];
168
            }
169
        }
170
 
171
        Console::getOutput()->writeAll(
172
            \str_replace(
173
                ['%i%p1%d', '%p2%d'],
174
                [$y, $x],
175
                Console::getTput()->get('cursor_address')
176
            )
177
        );
178
    }
179
 
180
    /**
181
     * Get current position (x and y) of the cursor.
182
     */
183
    public static function getPosition(): array
184
    {
185
        $tput = Console::getTput();
186
        $user7 = $tput->get('user7');
187
 
188
        if (null === $user7) {
189
            return [
190
                'x' => 0,
191
                'y' => 0,
192
            ];
193
        }
194
 
195
        Console::getOutput()->writeAll($user7);
196
 
197
        $input = Console::getInput();
198
 
199
        // Read $tput->get('user6').
200
        $input->read(2); // skip \033 and [.
201
 
202
        $x = null;
203
        $y = null;
204
        $handle = &$y;
205
 
206
        while (true) {
207
            $char = $input->readCharacter();
208
 
209
            switch ($char) {
210
                case ';':
211
                    $handle = &$x;
212
 
213
                    break;
214
 
215
                case 'R':
216
                    break 2;
217
 
218
                default:
219
                    $handle .= $char;
220
            }
221
        }
222
 
223
        return [
224
            'x' => (int) $x,
225
            'y' => (int) $y,
226
        ];
227
    }
228
 
229
    /**
230
     * Save current position.
231
     */
232
    public static function save()
233
    {
234
        Console::getOutput()->writeAll(
235
            Console::getTput()->get('save_cursor')
236
        );
237
    }
238
 
239
    /**
240
     * Restore cursor to the last saved position.
241
     */
242
    public static function restore()
243
    {
244
        Console::getOutput()->writeAll(
245
            Console::getTput()->get('restore_cursor')
246
        );
247
    }
248
 
249
    /**
250
     * Clear the screen.
251
     * Part can be:
252
     *     • a, all,   ↕ : clear entire screen and static::move(1, 1);
253
     *     • u, up,    ↑ : clear from cursor to beginning of the screen;
254
     *     • r, right, → : clear from cursor to the end of the line;
255
     *     • d, down,  ↓ : clear from cursor to end of the screen;
256
     *     • l, left,  ← : clear from cursor to beginning of the screen;
257
     *     •    line,  ↔ : clear all the line and static::move(1).
258
     * Parts can be concatenated by a single space.
259
     */
260
    public static function clear(string $parts = 'all')
261
    {
262
        $tput = Console::getTput();
263
        $output = Console::getOutput();
264
 
265
        foreach (\explode(' ', $parts) as $part) {
266
            switch ($part) {
267
                case 'a':
268
                case 'all':
269
                case '↕':
270
                    $output->writeAll($tput->get('clear_screen'));
271
                    static::moveTo(1, 1);
272
 
273
                    break;
274
 
275
                case 'u':
276
                case 'up':
277
                case '↑':
278
                    $output->writeAll("\033[1J");
279
 
280
                    break;
281
 
282
                case 'r':
283
                case 'right':
284
                case '→':
285
                    $output->writeAll($tput->get('clr_eol'));
286
 
287
                    break;
288
 
289
                case 'd':
290
                case 'down':
291
                case '↓':
292
                    $output->writeAll($tput->get('clr_eos'));
293
 
294
                    break;
295
 
296
                case 'l':
297
                case 'left':
298
                case '←':
299
                    $output->writeAll($tput->get('clr_bol'));
300
 
301
                    break;
302
 
303
                case 'line':
304
                case '↔':
305
                    $output->writeAll("\r".$tput->get('clr_eol'));
306
 
307
                    break;
308
            }
309
        }
310
    }
311
 
312
    /**
313
     * Hide the cursor.
314
     */
315
    public static function hide()
316
    {
317
        Console::getOutput()->writeAll(
318
            Console::getTput()->get('cursor_invisible')
319
        );
320
    }
321
 
322
    /**
323
     * Show the cursor.
324
     */
325
    public static function show()
326
    {
327
        Console::getOutput()->writeAll(
328
            Console::getTput()->get('cursor_visible')
329
        );
330
    }
331
 
332
    /**
333
     * Colorize cursor.
334
     * Attributes can be:
335
     *     •  n,         normal           : normal;
336
     *     •  b,         bold             : bold;
337
     *     •  u,         underlined       : underlined;
338
     *     •  bl,        blink            : blink;
339
     *     •  i,         inverse          : inverse;
340
     *     • !b,        !bold             : normal weight;
341
     *     • !u,        !underlined       : not underlined;
342
     *     • !bl,       !blink            : steady;
343
     *     • !i,        !inverse          : positive;
344
     *     • fg(color), foreground(color) : set foreground to “color”;
345
     *     • bg(color), background(color) : set background to “color”.
346
     * “color” can be:
347
     *     • default;
348
     *     • black;
349
     *     • red;
350
     *     • green;
351
     *     • yellow;
352
     *     • blue;
353
     *     • magenta;
354
     *     • cyan;
355
     *     • white;
356
     *     • 0-256 (classic palette);
357
     *     • #hexa.
358
     * Attributes can be concatenated by a single space.
359
     */
360
    public static function colorize(string $attributes)
361
    {
362
        static $_rgbTo256 = null;
363
 
364
        if (null === $_rgbTo256) {
365
            $_rgbTo256 = [
366
                '000000', '800000', '008000', '808000', '000080', '800080',
367
                '008080', 'c0c0c0', '808080', 'ff0000', '00ff00', 'ffff00',
368
                '0000ff', 'ff00ff', '00ffff', 'ffffff', '000000', '00005f',
369
                '000087', '0000af', '0000d7', '0000ff', '005f00', '005f5f',
370
                '005f87', '005faf', '005fd7', '005fff', '008700', '00875f',
371
                '008787', '0087af', '0087d7', '0087ff', '00af00', '00af5f',
372
                '00af87', '00afaf', '00afd7', '00afff', '00d700', '00d75f',
373
                '00d787', '00d7af', '00d7d7', '00d7ff', '00ff00', '00ff5f',
374
                '00ff87', '00ffaf', '00ffd7', '00ffff', '5f0000', '5f005f',
375
                '5f0087', '5f00af', '5f00d7', '5f00ff', '5f5f00', '5f5f5f',
376
                '5f5f87', '5f5faf', '5f5fd7', '5f5fff', '5f8700', '5f875f',
377
                '5f8787', '5f87af', '5f87d7', '5f87ff', '5faf00', '5faf5f',
378
                '5faf87', '5fafaf', '5fafd7', '5fafff', '5fd700', '5fd75f',
379
                '5fd787', '5fd7af', '5fd7d7', '5fd7ff', '5fff00', '5fff5f',
380
                '5fff87', '5fffaf', '5fffd7', '5fffff', '870000', '87005f',
381
                '870087', '8700af', '8700d7', '8700ff', '875f00', '875f5f',
382
                '875f87', '875faf', '875fd7', '875fff', '878700', '87875f',
383
                '878787', '8787af', '8787d7', '8787ff', '87af00', '87af5f',
384
                '87af87', '87afaf', '87afd7', '87afff', '87d700', '87d75f',
385
                '87d787', '87d7af', '87d7d7', '87d7ff', '87ff00', '87ff5f',
386
                '87ff87', '87ffaf', '87ffd7', '87ffff', 'af0000', 'af005f',
387
                'af0087', 'af00af', 'af00d7', 'af00ff', 'af5f00', 'af5f5f',
388
                'af5f87', 'af5faf', 'af5fd7', 'af5fff', 'af8700', 'af875f',
389
                'af8787', 'af87af', 'af87d7', 'af87ff', 'afaf00', 'afaf5f',
390
                'afaf87', 'afafaf', 'afafd7', 'afafff', 'afd700', 'afd75f',
391
                'afd787', 'afd7af', 'afd7d7', 'afd7ff', 'afff00', 'afff5f',
392
                'afff87', 'afffaf', 'afffd7', 'afffff', 'd70000', 'd7005f',
393
                'd70087', 'd700af', 'd700d7', 'd700ff', 'd75f00', 'd75f5f',
394
                'd75f87', 'd75faf', 'd75fd7', 'd75fff', 'd78700', 'd7875f',
395
                'd78787', 'd787af', 'd787d7', 'd787ff', 'd7af00', 'd7af5f',
396
                'd7af87', 'd7afaf', 'd7afd7', 'd7afff', 'd7d700', 'd7d75f',
397
                'd7d787', 'd7d7af', 'd7d7d7', 'd7d7ff', 'd7ff00', 'd7ff5f',
398
                'd7ff87', 'd7ffaf', 'd7ffd7', 'd7ffff', 'ff0000', 'ff005f',
399
                'ff0087', 'ff00af', 'ff00d7', 'ff00ff', 'ff5f00', 'ff5f5f',
400
                'ff5f87', 'ff5faf', 'ff5fd7', 'ff5fff', 'ff8700', 'ff875f',
401
                'ff8787', 'ff87af', 'ff87d7', 'ff87ff', 'ffaf00', 'ffaf5f',
402
                'ffaf87', 'ffafaf', 'ffafd7', 'ffafff', 'ffd700', 'ffd75f',
403
                'ffd787', 'ffd7af', 'ffd7d7', 'ffd7ff', 'ffff00', 'ffff5f',
404
                'ffff87', 'ffffaf', 'ffffd7', 'ffffff', '080808', '121212',
405
                '1c1c1c', '262626', '303030', '3a3a3a', '444444', '4e4e4e',
406
                '585858', '606060', '666666', '767676', '808080', '8a8a8a',
407
                '949494', '9e9e9e', 'a8a8a8', 'b2b2b2', 'bcbcbc', 'c6c6c6',
408
                'd0d0d0', 'dadada', 'e4e4e4', 'eeeeee',
409
            ];
410
        }
411
 
412
        $tput = Console::getTput();
413
 
414
        if (1 >= $tput->count('max_colors')) {
415
            return;
416
        }
417
 
418
        $handle = [];
419
 
420
        foreach (\explode(' ', $attributes) as $attribute) {
421
            switch ($attribute) {
422
                case 'n':
423
                case 'normal':
424
                    $handle[] = 0;
425
 
426
                    break;
427
 
428
                case 'b':
429
                case 'bold':
430
                    $handle[] = 1;
431
 
432
                    break;
433
 
434
                case 'u':
435
                case 'underlined':
436
                    $handle[] = 4;
437
 
438
                    break;
439
 
440
                case 'bl':
441
                case 'blink':
442
                    $handle[] = 5;
443
 
444
                    break;
445
 
446
                case 'i':
447
                case 'inverse':
448
                    $handle[] = 7;
449
 
450
                    break;
451
 
452
                case '!b':
453
                case '!bold':
454
                    $handle[] = 22;
455
 
456
                    break;
457
 
458
                case '!u':
459
                case '!underlined':
460
                    $handle[] = 24;
461
 
462
                    break;
463
 
464
                case '!bl':
465
                case '!blink':
466
                    $handle[] = 25;
467
 
468
                    break;
469
 
470
                case '!i':
471
                case '!inverse':
472
                    $handle[] = 27;
473
 
474
                    break;
475
 
476
                default:
477
                    if (0 === \preg_match('#^([^\(]+)\(([^\)]+)\)$#', $attribute, $m)) {
478
                        break;
479
                    }
480
 
481
                    $shift = 0;
482
 
483
                    switch ($m[1]) {
484
                        case 'fg':
485
                        case 'foreground':
486
                            $shift = 0;
487
 
488
                            break;
489
 
490
                        case 'bg':
491
                        case 'background':
492
                            $shift = 10;
493
 
494
                            break;
495
 
496
                        default:
497
                            break 2;
498
                    }
499
 
500
                    $_handle = 0;
501
                    $_keyword = true;
502
 
503
                    switch ($m[2]) {
504
                        case 'black':
505
                            $_handle = 30;
506
 
507
                            break;
508
 
509
                        case 'red':
510
                            $_handle = 31;
511
 
512
                            break;
513
 
514
                        case 'green':
515
                            $_handle = 32;
516
 
517
                            break;
518
 
519
                        case 'yellow':
520
                            $_handle = 33;
521
 
522
                            break;
523
 
524
                        case 'blue':
525
                            $_handle = 34;
526
 
527
                            break;
528
 
529
                        case 'magenta':
530
                            $_handle = 35;
531
 
532
                            break;
533
 
534
                        case 'cyan':
535
                            $_handle = 36;
536
 
537
                            break;
538
 
539
                        case 'white':
540
                            $_handle = 37;
541
 
542
                            break;
543
 
544
                        case 'default':
545
                            $_handle = 39;
546
 
547
                            break;
548
 
549
                        default:
550
                            $_keyword = false;
551
 
552
                            if (256 <= $tput->count('max_colors') &&
553
                                '#' === $m[2][0]) {
554
                                $rgb = \hexdec(\substr($m[2], 1));
555
                                $r = ($rgb >> 16) & 255;
556
                                $g = ($rgb >> 8) & 255;
557
                                $b = $rgb & 255;
558
                                $distance = null;
559
 
560
                                foreach ($_rgbTo256 as $i => $_rgb) {
561
                                    $_rgb = \hexdec($_rgb);
562
                                    $_r = ($_rgb >> 16) & 255;
563
                                    $_g = ($_rgb >> 8) & 255;
564
                                    $_b = $_rgb & 255;
565
 
566
                                    $d = \sqrt(
567
                                        ($_r - $r) ** 2
568
                                      + ($_g - $g) ** 2
569
                                      + ($_b - $b) ** 2
570
                                    );
571
 
572
                                    if (null === $distance ||
573
                                        $d <= $distance) {
574
                                        $distance = $d;
575
                                        $_handle = $i;
576
                                    }
577
                                }
578
                            } else {
579
                                $_handle = (int) ($m[2]);
580
                            }
581
                    }
582
 
583
                    if (true === $_keyword) {
584
                        $handle[] = $_handle + $shift;
585
                    } else {
586
                        $handle[] = (38 + $shift).';5;'.$_handle;
587
                    }
588
            }
589
        }
590
 
591
        Console::getOutput()->writeAll("\033[".\implode(';', $handle).'m');
592
 
593
        return;
594
    }
595
 
596
    /**
597
     * Change color number to a specific RGB color.
598
     */
599
    public static function changeColor(int $fromCode, int $toColor)
600
    {
601
        $tput = Console::getTput();
602
 
603
        if (true !== $tput->has('can_change')) {
604
            return;
605
        }
606
 
607
        $r = ($toColor >> 16) & 255;
608
        $g = ($toColor >> 8) & 255;
609
        $b = $toColor & 255;
610
 
611
        Console::getOutput()->writeAll(
612
            \str_replace(
613
                [
614
                    '%p1%d',
615
                    'rgb:',
616
                    '%p2%{255}%*%{1000}%/%2.2X/',
617
                    '%p3%{255}%*%{1000}%/%2.2X/',
618
                    '%p4%{255}%*%{1000}%/%2.2X',
619
                ],
620
                [
621
                    $fromCode,
622
                    '',
623
                    \sprintf('%02x', $r),
624
                    \sprintf('%02x', $g),
625
                    \sprintf('%02x', $b),
626
                ],
627
                $tput->get('initialize_color')
628
            )
629
        );
630
 
631
        return;
632
    }
633
 
634
    /**
635
     * Set cursor style.
636
     * Style can be:
637
     *     • b, block,     ▋: block;
638
     *     • u, underline, _: underline;
639
     *     • v, vertical,  |: vertical.
640
     */
641
    public static function setStyle(string $style, bool $blink = true)
642
    {
643
        if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
644
            return;
645
        }
646
 
647
        switch ($style) {
648
            case 'u':
649
            case 'underline':
650
            case '_':
651
                $_style = 2;
652
 
653
                break;
654
 
655
            case 'v':
656
            case 'vertical':
657
            case '|':
658
                $_style = 5;
659
 
660
                break;
661
 
662
            case 'b':
663
            case 'block':
664
            case '▋':
665
            default:
666
                $_style = 1;
667
 
668
                break;
669
        }
670
 
671
        if (false === $blink) {
672
            ++$_style;
673
        }
674
 
675
        // Not sure what tput entry we can use here…
676
        Console::getOutput()->writeAll("\033[".$_style.' q');
677
 
678
        return;
679
    }
680
 
681
    /**
682
     * Make a stupid “bip”.
683
     */
684
    public static function bip()
685
    {
686
        Console::getOutput()->writeAll(
687
            Console::getTput()->get('bell')
688
        );
689
    }
690
}
691
 
692
/*
693
 * Advanced interaction.
694
 */
695
Console::advancedInteraction();