summaryrefslogtreecommitdiff
path: root/src/bin/termpty.h
blob: ca1605c4d9b08a33a689b3f43ee4e5eb3c49a768 (plain)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
#ifndef _TERMPTY_H__
#define _TERMPTY_H__ 1

#include "config.h"
#include "media.h"
#include "sb.h"

typedef struct _Termcell      Termcell;
typedef struct _Termatt       Termatt;
typedef struct _Termsave      Termsave;
typedef struct _Termsavecomp  Termsavecomp;
typedef struct _Termblock     Termblock;
typedef struct _Termexp       Termexp;
typedef struct _Termpty       Termpty;
typedef struct _Termlink      Term_Link;

#define COL_DEF        0
#define COL_BLACK      1
#define COL_RED        2
#define COL_GREEN      3
#define COL_YELLOW     4
#define COL_BLUE       5
#define COL_MAGENTA    6
#define COL_CYAN       7
#define COL_WHITE      8
#define COL_INVIS      9

#define COL_INVERSE   10
#define COL_INVERSEBG 11

#define MOUSE_OFF              0
#define MOUSE_X10              1 // Press only
#define MOUSE_NORMAL           2 // Press+release only
#define MOUSE_NORMAL_BTN_MOVE  3 // Press+release+motion while pressed
#define MOUSE_NORMAL_ALL_MOVE  4 // Press+release+all motion

#define MOUSE_EXT_NONE         0
#define MOUSE_EXT_UTF8         1
#define MOUSE_EXT_SGR          2
#define MOUSE_EXT_URXVT        3

// Only for testing purpose
//#define SUPPORT_80_132_COLUMNS 1

#define MOVIE_STATE_PLAY   0
#define MOVIE_STATE_PAUSE  1
#define MOVIE_STATE_STOP   2

#define HL_LINKS_MAX  (1 << 16)

struct _Termlink
{
    const char *key;
    const char *url;
    unsigned int refcount;
};




struct _Termatt
{
   unsigned char fg, bg;
   unsigned short bold : 1;
   unsigned short faint : 1;
   unsigned short italic : 1;
   unsigned short dblwidth : 1;
   unsigned short underline : 1;
   unsigned short blink : 1; // don't intend to support this currently
   unsigned short blink2 : 1; // don't intend to support this currently
   unsigned short inverse : 1;
   unsigned short invisible : 1;
   unsigned short strike : 1;
   unsigned short fg256 : 1;
   unsigned short bg256 : 1;
   unsigned short fgintense : 1;
   unsigned short bgintense : 1;
   // below used for working out text from selections
   unsigned short autowrapped : 1;
   unsigned short newline : 1;
   unsigned short fraktur : 1;
   unsigned short framed : 1;
   unsigned short encircled : 1;
   unsigned short overlined : 1; // TODO: support it
   unsigned short tab_inserted : 1;
   unsigned short tab_last : 1;
#if defined(SUPPORT_80_132_COLUMNS)
   unsigned short is_80_132_mode_allowed : 1;
   unsigned short bit_padding :  9;
#else
   unsigned short bit_padding : 10;
#endif
   uint16_t       link_id;
};

typedef struct _Backlog_Beacon{
    int screen_y;
    int backlog_y;
} Backlog_Beacon;

typedef struct _Term_State {
    Termatt       att;
    unsigned char charset;
    unsigned char charsetch;
    unsigned char chset[4];
    int           top_margin, bottom_margin;
    int           left_margin, right_margin;
    int           had_cr_x, had_cr_y;
    unsigned int  lr_margins : 1;
    unsigned int  restrict_cursor : 1;
    unsigned int  multibyte : 1;
    unsigned int  alt_kp : 1;
    unsigned int  insert : 1;
    unsigned int  appcursor : 1;
    unsigned int  wrap : 1;
    unsigned int  wrapnext : 1;
    unsigned int  crlf : 1;
    unsigned int  send_bs : 1;
    unsigned int  kbd_lock : 1;
    unsigned int  reverse : 1;
    unsigned int  no_autorepeat : 1;
    unsigned int  cjk_ambiguous_wide : 1;
    unsigned int  hide_cursor : 1;
    unsigned int  combining_strike : 1;
    unsigned int  sace_rectangular : 1;
} Term_State;

typedef struct _Term_Cursor {
    int cx;
    int cy;
} Term_Cursor;

struct _Termpty
{
   Evas_Object *obj;
   Config *config;
   Ecore_Event_Handler *hand_exe_exit;
   Ecore_Fd_Handler *hand_fd;
   struct {
      struct {
         void (*func) (void *data);
         void *data;
      } change, set_title, set_icon, cancel_sel, exited, bell, command;
   } cb;
   struct {
      const char *icon;
      /* dynamic title set by xterm, keep it in case user don't want a
       * title any more by setting a empty title
       */
      const char *title;
      /* set by user */
      const char *user_title;
   } prop;
   const char *cur_cmd;
   Termcell *screen, *screen2;
   unsigned int *tabs;
   int circular_offset;
   int circular_offset2;
   Eina_Unicode *buf;
   size_t buflen;
   Eina_Unicode last_char;
   Eina_Bool buf_have_zero;
   unsigned char oldbuf[4];
   Termsave *back;
   size_t backsize, backpos;
   /* this beacon in the backlog tells about the top line in screen
    * coordinates that maps to a line in the backlog */
   Backlog_Beacon backlog_beacon;
   int w, h;
   int fd, slavefd;
#if defined(ENABLE_TESTS)
   struct ty_sb write_buffer;
#endif
#if defined(ENABLE_FUZZING)
   int fd_dev_null;
#endif
   struct {
      int curid;
      Eina_Hash *blocks;
      Eina_Hash *chid_map;
      Eina_List *active;
      Eina_List *expecting;
      unsigned char on : 1;
   } block;
   struct {
      /* start is always the start of the selection
       * so end.y can be < start.y */
      struct {
         int x, y;
      } start, end, orig;
      Eina_Unicode *codepoints;
      time_t last_click;
      unsigned char is_box    : 1;
      unsigned char is_active : 1; // there is a visible selection
      unsigned char makesel   : 1; // selection is being worked on
      unsigned char by_word   : 1;
      unsigned char by_line   : 1;
      unsigned char is_top_to_bottom : 1;
   } selection;
   Term_State termstate;
   Term_Cursor cursor_state;
   Term_Cursor cursor_save[2];
   int exit_code;
   pid_t pid;
   unsigned int altbuf     : 1;
   unsigned int mouse_mode : 3;
   unsigned int mouse_ext  : 2;
   unsigned int bracketed_paste : 1;
   unsigned int decoding_error : 1;
   struct {
       Term_Link *links;
       uint8_t *bitmap;
       uint32_t size;
   } hl;
};

struct _Termcell
{
   Eina_Unicode   codepoint;
   Termatt        att;
};

struct _Termsave
{
   unsigned int   gen  : 8;
   unsigned int   comp : 1;
   unsigned int   z    : 1;
   unsigned int   w    : 22;
   /* TODO: union ? */
   Termcell       *cells;
};

/* TODO: RESIZE rewrite Termsavecomp */
struct _Termsavecomp
{
   unsigned int   gen  : 8;
   unsigned int   comp : 1;
   unsigned int   z    : 1;
   unsigned int   w    : 22; // compressed size in bytes
   unsigned int   wout; // output width in Termcells
};

struct _Termblock
{
   Termpty     *pty;
   const char  *path, *link, *chid;
   Evas_Object *obj;
   Eina_List   *cmds;
   int          id;
   Media_Type   type;
   int          refs;
   short        w, h;
   short        x, y;
   unsigned char scale_stretch : 1;
   unsigned char scale_center : 1;
   unsigned char scale_fill : 1;
   unsigned char thumb : 1;
   unsigned char edje : 1;

   unsigned char active : 1;
   unsigned char was_active : 1;
   unsigned char was_active_before : 1;

   unsigned char mov_state : 2;  // movie state marker
};

struct _Termexp
{
   Eina_Unicode ch;
   int left, id;
   int x, y, w, h;
};


void       termpty_init(void);
void       termpty_shutdown(void);

Termpty   *termpty_new(const char *cmd, Eina_Bool login_shell, const char *cd,
                       int w, int h, Config *config, const char *emotion_mod,
                       const char *title, Ecore_Window window_id);
void       termpty_free(Termpty *ty);
void       termpty_config_update(Termpty *ty, Config *config);

void       termpty_backlog_lock(void);
void       termpty_backlog_unlock(void);

Termcell  *termpty_cellrow_get(Termpty *ty, int y, ssize_t *wret);
Termcell * termpty_cell_get(Termpty *ty, int y_requested, int x_requested);
ssize_t termpty_row_length(Termpty *ty, int y);
void       termpty_write(Termpty *ty, const char *input, int len);
void       termpty_resize(Termpty *ty, int new_w, int new_h);
void       termpty_resize_tabs(Termpty *ty, int old_w, int new_w);
void       termpty_backlog_size_set(Termpty *ty, size_t size);
ssize_t    termpty_backlog_length(Termpty *ty);
void       termpty_backscroll_adjust(Termpty *ty, int *scroll);

pid_t      termpty_pid_get(const Termpty *ty);
void       termpty_block_free(Termblock *tb);
Termblock *termpty_block_new(Termpty *ty, int w, int h, const char *path, const char *link);
void       termpty_block_insert(Termpty *ty, int ch, Termblock *blk);
int        termpty_block_id_get(const Termcell *cell, int *x, int *y);
Termblock *termpty_block_get(const Termpty *ty, int id);
void       termpty_block_chid_update(Termpty *ty, Termblock *blk);
Termblock *termpty_block_chid_get(const Termpty *ty, const char *chid);

void       termpty_cell_codepoint_att_fill(Termpty *ty, Eina_Unicode codepoint, Termatt att, Termcell *dst, int n);
void       termpty_cells_set_content(Termpty *ty, Termcell *cells,
                          Eina_Unicode codepoint, int count);
void       termpty_screen_swap(Termpty *ty);

ssize_t termpty_line_length(const Termcell *cells, ssize_t nb_cells);

void termpty_handle_buf(Termpty *ty, const Eina_Unicode *codepoints, int len);
void termpty_handle_block_codepoint_overwrite_heavy(Termpty *ty, int oldc, int newc);

Term_Link * term_link_new(Termpty *ty);
void term_link_free(Termpty *ty, Term_Link *link);

int
termpty_color_class_get(Termpty *ty, const char *key,
                        int *r, int *g, int *b, int *a);

extern int _termpty_log_dom;

#define TERMPTY_SCREEN(Tpty, X, Y) \
  Tpty->screen[X + (((Y + Tpty->circular_offset) % Tpty->h) * Tpty->w)]

#define TERMPTY_RESTRICT_FIELD(Field, Min, Max) \
   do {                                         \
   if (Field >= Max)                            \
     Field = Max - 1;                           \
   else if (Field < Min)                        \
     Field = Min;                               \
   } while (0)

/* Try to trick the compiler into inlining the first test */
#define HANDLE_BLOCK_CODEPOINT_OVERWRITE(Tpty, OLDC, NEWC)                   \
do {                                                                         \
   if (EINA_UNLIKELY((OLDC | NEWC) & 0x80000000))                            \
       termpty_handle_block_codepoint_overwrite_heavy(Tpty, OLDC, NEWC);     \
} while (0)

#define TERMPTY_CELL_COPY(Tpty, Tsrc, Tdst, N)                               \
do {                                                                         \
   int __i;                                                                  \
                                                                             \
   for (__i = 0; __i < N; __i++)                                             \
     {                                                                       \
        HANDLE_BLOCK_CODEPOINT_OVERWRITE(Tpty,                               \
                                         (Tdst)[__i].codepoint,              \
                                         (Tsrc)[__i].codepoint);             \
        if (EINA_UNLIKELY((Tdst)[__i].att.link_id))                          \
          term_link_refcount_dec(ty, (Tdst)[__i].att.link_id, 1);            \
        if (EINA_UNLIKELY((Tsrc)[__i].att.link_id))                          \
          term_link_refcount_inc(ty, (Tsrc)[__i].att.link_id, 1);            \
     }                                                                       \
   memcpy(Tdst, Tsrc, N * sizeof(Termcell));                                 \
} while (0)


static inline void
term_link_refcount_inc(Termpty *ty, uint16_t link_id, uint16_t count)
{
   Term_Link *link;

   link = &ty->hl.links[link_id];
   link->refcount += count;
}

static inline void
term_link_refcount_dec(Termpty *ty, uint16_t link_id, uint16_t count)
{
   Term_Link *link;

   link = &ty->hl.links[link_id];
   link->refcount -= count;
   if (EINA_UNLIKELY(link->refcount == 0))
     term_link_free(ty, link);
}

static inline Eina_Bool
term_link_eq(Termpty *ty, Term_Link *hl, uint16_t link_id)
{
    Term_Link *hl2;
    uint16_t hl_id;

    if (link_id == 0)
        return EINA_FALSE;

    hl_id = hl - ty->hl.links;
    if (hl_id == link_id)
        return EINA_TRUE;
    hl2 = &ty->hl.links[link_id];
    if (!hl->key || !hl2->key ||
        strcmp(hl->key, hl2->key) != 0)
        return EINA_FALSE;
    return (strcmp(hl->url, hl2->url) == 0);
}

static inline void
termpty_cell_fill(Termpty *ty, Termcell *src, Termcell *dst, int n)
{
   int i;

   if (src)
     {
        for (i = 0; i < n; i++)
          {
             HANDLE_BLOCK_CODEPOINT_OVERWRITE(ty, dst[i].codepoint, src[0].codepoint);
             if (EINA_UNLIKELY(dst[i].att.link_id))
               term_link_refcount_dec(ty, dst[i].att.link_id, 1);

             dst[i] = src[0];
          }
        if (src[0].att.link_id)
          term_link_refcount_inc(ty, src[0].att.link_id, n);
     }
   else
     {
        for (i = 0; i < n; i++)
          {
             HANDLE_BLOCK_CODEPOINT_OVERWRITE(ty, dst[i].codepoint, 0);
             if (EINA_UNLIKELY(dst[i].att.link_id))
               term_link_refcount_dec(ty, dst[i].att.link_id, 1);

             memset(&(dst[i]), 0, sizeof(*dst));
          }
     }
}
#endif