1#  You may distribute under the terms of either the GNU General Public License
2#  or the Artistic License (the same terms as Perl itself)
3#
4#  (C) Paul Evans, 2009-2020 -- leonerd@leonerd.org.uk
5
6package Tickit::Term 0.72;
7
8use v5.14;
9use warnings;
10
11use Carp;
12
13# Load the XS code
14use Tickit qw( MOD_SHIFT MOD_ALT MOD_CTRL BIND_FIRST );
15
16# We export some constants
17use Exporter 'import';
18
19# Old names for these
20use constant {
21   TERM_CURSORSHAPE_BLOCK    => CURSORSHAPE_BLOCK,
22   TERM_CURSORSHAPE_UNDER    => CURSORSHAPE_UNDER,
23   TERM_CURSORSHAPE_LEFT_BAR => CURSORSHAPE_LEFT_BAR,
24};
25
26push our @EXPORT_OK, qw(
27   TERM_CURSORSHAPE_BLOCK TERM_CURSORSHAPE_UNDER TERM_CURSORSHAPE_LEFT_BAR
28   MOD_SHIFT MOD_ALT MOD_CTRL
29   BIND_FIRST
30);
31
32=head1 NAME
33
34C<Tickit::Term> - terminal formatting abstraction
35
36=head1 SYNOPSIS
37
38=head1 DESCRIPTION
39
40Provides terminal control primitives for L<Tickit>; a number of methods that
41control the terminal by writing control strings. This object itself performs
42no actual IO work; it writes bytes to a delegated object given to the
43constructor called the writer.
44
45This object is not normally constructed directly by the containing
46application; instead it is used indirectly by other parts of the C<Tickit>
47distribution.
48
49Note that a given program may contain multiple objects in this class that all
50refer to the same underlying C<TickitTerm> instance from the C library. This
51is especially true of the first argument provided to event binding callbacks.
52This class overloads numify and stringify operations, so that instances may be
53compared using the C<==> or C<eq> operators, or used as keys in hashes, and
54they will act as expected. Do not rely on plain C<refaddr> comparison however
55as you may get incorrect results.
56
57=cut
58
59use overload
60   '0+' => "_xs_addr",
61   '""' => sub { sprintf "Tickit::Term=XS(tt=0x%x)", $_[0]->_xs_addr },
62   fallback => 1;
63
64=head1 CONSTRUCTOR
65
66=cut
67
68=head2 new
69
70   $term = Tickit::Term->new( %params )
71
72Constructs a new C<Tickit::Term> object.
73
74Takes the following named arguments at construction time:
75
76=over 8
77
78=item UTF8 => BOOL
79
80If defined, overrides locale detection to enable or disable UTF-8 mode. If not
81defined then this will be detected from the locale by using Perl's
82C<${^UTF8LOCALE}> variable.
83
84=item writer => OBJECT
85
86An object delegated to for sending strings of terminal control bytes to the
87terminal itself. This object must support a single method, C<write>, taking
88a string of bytes.
89
90 $writer->write( $data )
91
92Such an interface is supported by an C<IO::Handle> object.
93
94=item output_handle => HANDLE
95
96Optional. If supplied, will be used as the terminal filehandle for querying
97the size. Even if supplied, all writing operations will use the C<writer>
98function rather than performing IO operations on this filehandle.
99
100=item input_handle => HANDLE
101
102Optional. If supplied, will be used as the terminal filehandle for reading
103keypress and other events.
104
105=back
106
107=cut
108
109sub new
110{
111   my $class = shift;
112   my %params = @_;
113
114   return $class->_new(
115      $ENV{TERM}, @params{qw( input_handle output_handle writer UTF8 )}
116   ) || croak "Cannot construct Tickit::Term - $!";
117}
118
119=head2 open_stdio
120
121   $term = Tickit::Term->open_stdio
122
123Convenient shortcut for obtaining a L<Tickit::Term> instance bound to the
124STDIN and STDOUT streams of the process.
125
126=cut
127
128=head1 METHODS
129
130=cut
131
132=head2 get_input_handle
133
134   $fh = $term->get_input_handle
135
136Returns the input handle set by the C<input_handle> constructor arg.
137
138Note that because L<Tickit::Term> merely wraps an object provided by the
139lower-level F<libtickit> C library, it is no longer guaranteed that this
140method will return the same perl-level object that was given to the
141constructor. The object may be newly-constructed to represent a new perl-level
142readable filehandle on the same file number.
143
144=cut
145
146sub get_input_handle
147{
148   my $self = shift;
149   return IO::Handle->new_from_fd( $self->get_input_fd, "r" );
150}
151
152=head2 get_output_handle
153
154   $fh = $term->get_output_handle
155
156Returns the output handle set by the C<output_handle> constructor arg.
157
158Note that because L<Tickit::Term> merely wraps an object provided by the
159lower-level F<libtickit> C library, it is no longer guaranteed that this
160method will return the same perl-level object that was given to the
161constructor. The object may be newly-constructed to represent a new perl-level
162writable filehandle on the same file number.
163
164=cut
165
166sub get_output_handle
167{
168   my $self = shift;
169   return IO::Handle->new_from_fd( $self->get_output_fd, "w" );
170}
171
172=head2 set_output_buffer
173
174   $term->set_output_buffer( $len )
175
176Sets the size of the output buffer
177
178=cut
179
180=head2 await_started
181
182   $term->await_started( $timeout )
183
184Waits for the terminal startup process to complete, up to the timeout given in
185seconds.
186
187=cut
188
189=head2 pause
190
191   $term->pause
192
193Suspends operation of the terminal by resetting it to its default state.
194
195=cut
196
197=head2 resume
198
199   $term->resume
200
201Resumes operation of the terminal after a L</pause>.
202
203Typically these two methods are used together, either side of a blocking wait
204around a C<SIGSTOP>.
205
206   sub suspend
207   {
208      $term->pause;
209      kill STOP => $$;
210      $term->resume;
211      $rootwin->expose;
212   }
213
214=cut
215
216=head2 teardown
217
218   $term->teardown
219
220Shuts down operation of the terminal entirely, in preparation for terminating
221the process.
222
223=cut
224
225=head2 flush
226
227   $term->flush
228
229Flushes the output buffer to the terminal
230
231=cut
232
233=head2 bind_event
234
235   $id = $term->bind_event( $ev, $code, $data )
236
237Installs a new event handler to watch for the event specified by C<$ev>,
238invoking the C<$code> reference when it occurs. C<$code> will be invoked with
239the given terminal, the event name, an event information object, and the
240C<$data> value it was installed with. C<bind_event> returns an ID value that
241may be used to remove the handler by calling C<unbind_event_id>.
242
243 $ret = $code->( $term, $ev, $info, $data )
244
245The type of C<$info> will depend on the kind of event that was received, as
246indicated by C<$ev>. The information structure types are documented in
247L<Tickit::Event>.
248
249=head2 bind_event (with flags)
250
251   $id = $term->bind_event( $ev, $flags, $code, $data )
252
253The C<$code> argument may optionally be preceded by an integer of flag
254values. This should be zero to apply default semantics, or a bitmask of one or
255more of the following constants:
256
257=over 4
258
259=item TICKIT_BIND_FIRST
260
261Inserts this event handler first in the chain, before any existing ones.
262
263=item TICKIT_BIND_ONESHOT
264
265Remove the event handler after it has been invoked the first time.
266
267=back
268
269=head2 unbind_event_id
270
271   $term->unbind_event_id( $id )
272
273Removes an event handler that returned the given C<$id> value.
274
275=cut
276
277sub bind_event
278{
279   my $self = shift;
280   my $ev = shift;
281   my ( $flags, $code, $data ) = ( ref $_[0] ) ? ( 0, @_ ) : @_;
282
283   $self->_bind_event( $ev, $flags, $code, $data );
284}
285
286=head2 refresh_size
287
288   $term->refresh_size
289
290If a filehandle was supplied to the constructor, fetch the size of the
291terminal and update the cached sizes in the object. May invoke C<on_resize> if
292the new size is different.
293
294=cut
295
296=head2 set_size
297
298   $term->set_size( $lines, $cols )
299
300Defines the size of the terminal. Invoke C<on_resize> if the new size is
301different.
302
303=cut
304
305=head2 lines
306
307=head2 cols
308
309   $lines = $term->lines
310
311   $cols = $term->cols
312
313Query the size of the terminal, as set by the most recent C<refresh_size> or
314C<set_size> operation.
315
316=cut
317
318sub lines { ( shift->get_size )[0] }
319sub cols  { ( shift->get_size )[1]  }
320
321=head2 goto
322
323   $success = $term->goto( $line, $col )
324
325Move the cursor to the given position on the screen. If only one parameter is
326defined, does not alter the other. Both C<$line> and C<$col> are 0-based.
327
328Note that not all terminals can support these partial moves. This method
329returns a boolean indicating success; if the terminal could not perform the
330move it will need to be retried using a fully-specified call.
331
332=cut
333
334=head2 move
335
336   $term->move( $downward, $rightward )
337
338Move the cursor relative to where it currently is.
339
340=cut
341
342=head2 scrollrect
343
344   $success = $term->scrollrect( $top, $left, $lines, $cols, $downward, $rightward )
345
346Attempt to scroll the rectangle of the screen defined by the first four
347parameters by an amount given by the latter two. Since most terminals cannot
348perform arbitrary rectangle scrolling, this method returns a boolean to
349indicate if it was successful. The caller should test this return value and
350fall back to another drawing strategy if the attempt was unsuccessful.
351
352The cursor may move as a result of calling this method; its location is
353undefined if this method returns successful.
354
355=cut
356
357=head2 chpen
358
359   $term->chpen( $pen )
360
361   $term->chpen( %attrs )
362
363Changes the current pen attributes to those given. Any attribute whose value
364is given as C<undef> is reset. Any attributes not named are unchanged.
365
366For details of the supported pen attributes, see L<Tickit::Pen>.
367
368=cut
369
370=head2 setpen
371
372   $term->setpen( $pen )
373
374   $term->setpen( %attrs )
375
376Similar to C<chpen>, but completely defines the state of the terminal pen. Any
377attribute not given will be reset to its default value.
378
379=cut
380
381=head2 print
382
383   $term->print( $text, [ $pen ] )
384
385Print the given text to the terminal at the current cursor position.
386
387An optional C<Tickit::Pen> may be provided; if present it will be set as if
388given to C<setpen> first.
389
390=cut
391
392=head2 clear
393
394   $term->clear( [ $pen ] )
395
396Erase the entire screen.
397
398An optional C<Tickit::Pen> may be provided; if present it will be set as if
399given to C<setpen> first.
400
401=cut
402
403=head2 erasech
404
405   $term->erasech( $count, $moveend, [ $pen ] )
406
407Erase C<$count> characters forwards. If C<$moveend> is true, the cursor is
408moved to the end of the erased region. If defined but false, the cursor will
409remain where it is. If undefined, the terminal will perform whichever of these
410behaviours is more efficient, and the cursor will end at some undefined
411location.
412
413Using C<$moveend> may be more efficient than separate C<erasech> and C<goto>
414calls on terminals that do not have an erase function, as it will be
415implemented by printing spaces. This removes the need for two cursor jumps.
416
417An optional C<Tickit::Pen> may be provided; if present it will be set as if
418given to C<setpen> first.
419
420=cut
421
422=head2 getctl_int
423
424=head2 setctl_int
425
426   $value = $term->getctl_int( $ctl )
427
428   $success = $term->setctl_int( $ctl, $value )
429
430Gets or sets the value of an integer terminal control option. C<$ctl> should
431be one of the following options. They can be specified either as integers,
432using the following named constants, or as strings giving the part following
433C<TERMCTL_> in lower-case.
434
435On failure, each method returns C<undef>.
436
437=over 8
438
439=item TERMCTL_ALTSCREEN
440
441Enables DEC Alternate Screen mode
442
443=item TERMCTL_CURSORVIS
444
445Enables cursor visible mode
446
447=item TERMCTL_CURSORBLINK
448
449Enables cursor blinking mode
450
451=item TERMCTL_CURSORSHAPE
452
453Sets the shape of the cursor. C<$value> should be one of
454C<CURSORSHAPE_BLOCK>, C<CURSORSHAPE_UNDER> or C<CURSORSHAPE_LEFT_BAR>.
455
456=item TERMCTL_KEYPAD_APP
457
458Enables keypad application mode
459
460=item TERMCTL_MOUSE
461
462Enables mouse tracking mode. C<$vaule> should be one of
463C<TERM_MOUSEMODE_CLICK>, C<TERM_MOUSEMODE_DRAG>, C<TERM_MOUSEMODE_MOVE> or
464C<TERM_MOUSEMODE_OFF>.
465
466=back
467
468=head2 setctl_str
469
470   $success = $term->setctl_str( $ctl, $value )
471
472Sets the value of a string terminal control option. C<$ctrl> should be one of
473the following options. They can be specified either as integers or strings, as
474for C<setctl_int>.
475
476=over 8
477
478=item TERMCTL_ICON_TEXT
479
480=item TERMCTL_TITLE_TEXT
481
482=item TERMCTL_ICONTITLE_TEXT
483
484Sets the terminal window icon text, title, or both.
485
486=back
487
488=head2 getctl
489
490=head2 setctl
491
492   $value = $term->getctl( $ctl )
493
494   $success = $term->setctl( $ctl, $value )
495
496A newer form of the various typed get and set methods above. This version
497will interpret the given value as appropriate, depending on the control type.
498
499=cut
500
501=head2 input_push_bytes
502
503   $term->input_push_bytes( $bytes )
504
505Feeds more bytes of input. May result in C<key> or C<mouse> events.
506
507=cut
508
509=head2 input_readable
510
511   $term->input_readable
512
513Informs the term that the input handle may be readable. Attempts to read more
514bytes of input. May result in C<key> or C<mouse> events.
515
516=cut
517
518=head2 input_wait
519
520   $term->input_wait( $timeout )
521
522Block until some input is available, and process it. Returns after one round
523of input has been processed. May result in C<key> or C<mouse> events. If
524C<$timeout> is defined, it will wait a period of time no longer than this time
525before returning, even if no input events were received.
526
527=cut
528
529=head2 check_timeout
530
531   $timeout = $term->check_timeout
532
533Returns a number in seconds to represent when the next timeout should occur on
534the terminal, or C<undef> if nothing is waiting. May invoke expired timeouts,
535and cause a C<key> event to occur.
536
537=cut
538
539=head2 emit_key
540
541   $term->emit_key(
542      type => $type, str => $str, [ mod => $mod ]
543   )
544
545Invokes the key event handlers as if an event with the given info had just
546been received. The C<mod> argument is optional, a default of 0 will apply if
547it is missing.
548
549=cut
550
551sub emit_key
552{
553   my $self = shift;
554   my %args = @_;
555
556   $self->_emit_key( Tickit::Event::Key->_new(
557      $args{type}, $args{str}, $args{mod} // 0
558   ) );
559}
560
561=head2 emit_mouse
562
563   $term->emit_mouse(
564      type => $type, button => $button, line => $line, col => $col,
565      [ mod => $mod ]
566   )
567
568Invokes the mouse event handlers as if an event with the given info had just
569been received. The C<mod> argument is optional, a default of 0 will apply if
570it is missing.
571
572=cut
573
574sub emit_mouse
575{
576   my $self = shift;
577   my %args = @_;
578
579   $self->_emit_mouse( Tickit::Event::Mouse->_new(
580      $args{type}, $args{button}, $args{line}, $args{col}, $args{mod} // 0
581   ) );
582}
583
584=head1 EVENTS
585
586The following event types are emitted and may be observed by L</bind_event>.
587
588=head2 resize
589
590Emitted when the terminal itself has been resized.
591
592=head2 key
593
594Emitted when a key on the keyboard is pressed.
595
596=head2 mouse
597
598Emitted when a mouse button is pressed or released, the cursor moved while a
599button is held (a dragging event), or the wheel is scrolled.
600
601Behaviour of events involving more than one mouse button is not well-specified
602by terminals.
603
604=cut
605
606=head1 TODO
607
608=over 4
609
610=item *
611
612Track cursor position, and optimise (or eliminate entirely) C<goto> calls.
613
614=back
615
616=head1 AUTHOR
617
618Paul Evans <leonerd@leonerd.org.uk>
619
620=cut
621
6220x55AA;
623