]> git.proxmox.com Git - spiceterm.git/blame - spiceterm.c
use reasonable names (instead of 'test')
[spiceterm.git] / spiceterm.c
CommitLineData
22e5ba02
DM
1/*
2
3 Copyright (C) 2007-2011 Proxmox Server Solutions GmbH
4
5 Copyright: spiceterm is under GNU GPL, the GNU General Public License.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; version 2 dated June, 1991.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20
21 Author: Dietmar Maurer <dietmar@proxmox.com>
22
23*/
24
abc13312
DM
25#define _GNU_SOURCE
26
22e5ba02
DM
27#include <stdio.h>
28#include <stdlib.h>
29#include <unistd.h>
3affb0e7 30#include <sys/types.h>
22e5ba02
DM
31#include <sys/socket.h>
32#include <arpa/inet.h>
33#include <netdb.h>
34#include <pty.h> /* for openpty and forkpty */
35#include <string.h>
36#include <errno.h>
37#include <sys/ioctl.h>
38#include <sys/wait.h>
39#include <signal.h>
40#include <locale.h>
41
42#include "spiceterm.h"
22e5ba02 43
63a34f43 44#include <glib.h>
22e5ba02
DM
45#include <spice.h>
46#include <spice/enums.h>
47#include <spice/macros.h>
48#include <spice/qxl_dev.h>
49
c46a8251 50#include <gdk/gdkkeysyms.h>
22e5ba02 51
64bc7a2f 52#include "event_loop.h"
e3759a34 53#include "translations.h"
8c22ae7f 54
22e5ba02
DM
55/* define this for debugging */
56//#define DEBUG
57
58#define TERM "xterm"
59
60#define TERMIDCODE "[?1;2c" // vt100 ID
61
62#define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
63 fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
64 print_usage (NULL); \
65 exit(1); \
66}
67
68/* these colours are from linux kernel drivers/char/vt.c */
69
22e5ba02
DM
70unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
71 8,12,10,14, 9,13,11,15 };
72
22e5ba02
DM
73
74static void
75print_usage (const char *msg)
76{
77 if (msg) { fprintf (stderr, "ERROR: %s\n", msg); }
d6a5f5a9 78 fprintf (stderr, "USAGE: spiceterm [spiceopts] [-c command [args]]\n");
22e5ba02
DM
79}
80
81/* Convert UCS2 to UTF8 sequence, trailing zero */
abc13312 82/*
22e5ba02
DM
83static int
84ucs2_to_utf8 (unicode c, char *out)
85{
86 if (c < 0x80) {
87 out[0] = c; // 0*******
88 out[1] = 0;
89 return 1;
90 } else if (c < 0x800) {
91 out[0] = 0xc0 | (c >> 6); // 110***** 10******
92 out[1] = 0x80 | (c & 0x3f);
93 out[2] = 0;
94 return 2;
95 } else {
96 out[0] = 0xe0 | (c >> 12); // 1110**** 10****** 10******
97 out[1] = 0x80 | ((c >> 6) & 0x3f);
98 out[2] = 0x80 | (c & 0x3f);
99 out[3] = 0;
100 return 3;
101 }
102
103 return 0;
104}
abc13312 105*/
22e5ba02
DM
106
107static void
d6a5f5a9 108draw_char_at (spiceTerm *vt, int x, int y, unicode ch, TextAttributes attrib)
22e5ba02 109{
3affb0e7
DM
110 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) {
111 return;
22e5ba02
DM
112 }
113
64bc7a2f 114 spice_screen_draw_char(vt->screen, x, y, ch, attrib);
22e5ba02
DM
115}
116
117static void
d6a5f5a9 118spiceterm_update_xy (spiceTerm *vt, int x, int y)
22e5ba02
DM
119{
120 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
121
122 int y1 = (vt->y_base + y) % vt->total_height;
123 int y2 = y1 - vt->y_displ;
124 if (y2 < 0) {
125 y2 += vt->total_height;
126 }
127 if (y2 < vt->height) {
128 TextCell *c = &vt->cells[y1 * vt->width + x];
129 draw_char_at (vt, x, y2, c->ch, c->attrib);
130 }
131}
132
133static void
d6a5f5a9 134spiceterm_clear_xy (spiceTerm *vt, int x, int y)
22e5ba02
DM
135{
136 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
137
138 int y1 = (vt->y_base + y) % vt->total_height;
139 int y2 = y1 - vt->y_displ;
140 if (y2 < 0) {
141 y2 += vt->total_height;
142 }
143 if (y2 < vt->height) {
144 TextCell *c = &vt->cells[y1 * vt->width + x];
145 c->ch = ' ';
146 c->attrib = vt->default_attrib;
147 c->attrib.fgcol = vt->cur_attrib.fgcol;
148 c->attrib.bgcol = vt->cur_attrib.bgcol;
149
150 draw_char_at (vt, x, y, c->ch, c->attrib);
151 }
152}
153
154static void
d6a5f5a9 155spiceterm_show_cursor (spiceTerm *vt, int show)
22e5ba02
DM
156{
157 int x = vt->cx;
158 if (x >= vt->width) {
159 x = vt->width - 1;
160 }
161
162 int y1 = (vt->y_base + vt->cy) % vt->total_height;
163 int y = y1 - vt->y_displ;
164 if (y < 0) {
165 y += vt->total_height;
166 }
167
168 if (y < vt->height) {
169
170 TextCell *c = &vt->cells[y1 * vt->width + x];
171
172 if (show) {
173 TextAttributes attrib = vt->default_attrib;
174 attrib.invers = !(attrib.invers); /* invert fg and bg */
175 draw_char_at (vt, x, y, c->ch, attrib);
176 } else {
177 draw_char_at (vt, x, y, c->ch, c->attrib);
178 }
179 }
180}
181
182static void
d6a5f5a9 183spiceterm_refresh (spiceTerm *vt)
22e5ba02
DM
184{
185 int x, y, y1;
186
187 // rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol);
188
189 y1 = vt->y_displ;
190 for(y = 0; y < vt->height; y++) {
191 TextCell *c = vt->cells + y1 * vt->width;
192 for(x = 0; x < vt->width; x++) {
193 draw_char_at (vt, x, y, c->ch, c->attrib);
194 c++;
195 }
196 if (++y1 == vt->total_height)
197 y1 = 0;
198 }
199 //rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy);
200
d6a5f5a9 201 spiceterm_show_cursor (vt, 1);
22e5ba02
DM
202}
203
204static void
d6a5f5a9 205spiceterm_scroll_down (spiceTerm *vt, int top, int bottom, int lines)
22e5ba02 206{
7b4a7650
DM
207 if ((top + lines) >= bottom) {
208 lines = bottom - top -1;
209 }
22e5ba02 210
7b4a7650
DM
211 if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
212 return;
213 }
22e5ba02 214
7b4a7650
DM
215 int i;
216 for(i = bottom - top - lines - 1; i >= 0; i--) {
217 int src = ((vt->y_base + top + i) % vt->total_height)*vt->width;
218 int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
22e5ba02 219
7b4a7650
DM
220 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
221 }
22e5ba02 222
7b4a7650
DM
223 for (i = 0; i < lines; i++) {
224 int j;
225 TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width;
226 for(j = 0; j < vt->width; j++) {
227 c->attrib = vt->default_attrib;
228 c->ch = ' ';
229 c++;
230 }
231 }
22e5ba02 232
7b4a7650
DM
233 int h = lines * 16;
234 int y0 = top*16;
235 int y1 = y0 + h;
236 int y2 = bottom*16;
22e5ba02 237
64bc7a2f
DM
238 spice_screen_scroll(vt->screen, 0, y1, vt->screen->primary_width, y2, 0, y0);
239 spice_screen_clear(vt->screen, 0, y0, vt->screen->primary_width, y1);
22e5ba02
DM
240}
241
242static void
d6a5f5a9 243spiceterm_scroll_up (spiceTerm *vt, int top, int bottom, int lines, int moveattr)
22e5ba02 244{
7b4a7650
DM
245 if ((top + lines) >= bottom) {
246 lines = bottom - top - 1;
247 }
22e5ba02 248
7b4a7650
DM
249 if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
250 return;
251 }
22e5ba02 252
22e5ba02 253
7b4a7650
DM
254 int h = lines * 16;
255 int y0 = top*16;
256 int y1 = (top + lines)*16;
257 int y2 = bottom*16;
22e5ba02 258
64bc7a2f
DM
259 spice_screen_scroll(vt->screen, 0, y0, vt->screen->primary_width, y2 -h, 0, y1);
260 spice_screen_clear(vt->screen, 0, y2 -h, vt->screen->primary_width, y2);
7b4a7650
DM
261
262 if (!moveattr) {
263 return;
264 }
22e5ba02 265
7b4a7650 266 // move attributes
22e5ba02 267
7b4a7650
DM
268 int i;
269 for(i = 0; i < (bottom - top - lines); i++) {
270 int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width;
271 int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
22e5ba02 272
7b4a7650
DM
273 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
274 }
22e5ba02 275
7b4a7650
DM
276 for (i = 1; i <= lines; i++) {
277 int j;
278 TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width;
279 for(j = 0; j < vt->width; j++) {
280 c->attrib = vt->default_attrib;
281 c->ch = ' ';
282 c++;
283 }
22e5ba02 284 }
22e5ba02
DM
285}
286
287static void
d6a5f5a9 288spiceterm_virtual_scroll (spiceTerm *vt, int lines)
22e5ba02
DM
289{
290 if (vt->altbuf || lines == 0) return;
291
292 if (lines < 0) {
293 lines = -lines;
294 int i = vt->scroll_height;
295 if (i > vt->total_height - vt->height)
296 i = vt->total_height - vt->height;
297 int y1 = vt->y_base - i;
298 if (y1 < 0)
299 y1 += vt->total_height;
300 for(i = 0; i < lines; i++) {
301 if (vt->y_displ == y1) break;
302 if (--vt->y_displ < 0) {
303 vt->y_displ = vt->total_height - 1;
304 }
305 }
306 } else {
307 int i;
308 for(i = 0; i < lines; i++) {
309 if (vt->y_displ == vt->y_base) break;
310 if (++vt->y_displ == vt->total_height) {
311 vt->y_displ = 0;
312 }
313 }
314
315 }
316
d6a5f5a9 317 spiceterm_refresh (vt);
22e5ba02
DM
318}
319static void
d6a5f5a9 320spiceterm_respond_esc (spiceTerm *vt, const char *esc)
22e5ba02
DM
321{
322 int len = strlen (esc);
323 int i;
324
325 if (vt->ibuf_count < (IBUFSIZE - 1 - len)) {
326 vt->ibuf[vt->ibuf_count++] = 27;
327 for (i = 0; i < len; i++) {
328 vt->ibuf[vt->ibuf_count++] = esc[i];
329 }
330 }
331}
332
333static void
d6a5f5a9 334spiceterm_put_lf (spiceTerm *vt)
22e5ba02
DM
335{
336 if (vt->cy + 1 == vt->region_bottom) {
337
338 if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) {
d6a5f5a9 339 spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 1);
22e5ba02
DM
340 return;
341 }
342
343 if (vt->y_displ == vt->y_base) {
d6a5f5a9 344 spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 0);
22e5ba02
DM
345 }
346
347 if (vt->y_displ == vt->y_base) {
348 if (++vt->y_displ == vt->total_height) {
349 vt->y_displ = 0;
350 }
351 }
352
353 if (++vt->y_base == vt->total_height) {
354 vt->y_base = 0;
355 }
356
357 if (vt->scroll_height < vt->total_height) {
358 vt->scroll_height++;
359 }
360
361 int y1 = (vt->y_base + vt->height - 1) % vt->total_height;
362 TextCell *c = &vt->cells[y1 * vt->width];
363 int x;
364 for (x = 0; x < vt->width; x++) {
365 c->ch = ' ';
366 c->attrib = vt->default_attrib;
367 c++;
368 }
369
370 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
371
372 } else if (vt->cy < vt->height - 1) {
373 vt->cy += 1;
374 }
375}
376
377
378static void
d6a5f5a9 379spiceterm_csi_m (spiceTerm *vt)
22e5ba02
DM
380{
381 int i;
382
383 for (i = 0; i < vt->esc_count; i++) {
384 switch (vt->esc_buf[i]) {
385 case 0: /* reset all console attributes to default */
386 vt->cur_attrib = vt->default_attrib;
387 break;
388 case 1:
389 vt->cur_attrib.bold = 1;
390 break;
391 case 4:
392 vt->cur_attrib.uline = 1;
393 break;
394 case 5:
395 vt->cur_attrib.blink = 1;
396 break;
397 case 7:
398 vt->cur_attrib.invers = 1;
399 break;
400 case 8:
401 vt->cur_attrib.unvisible = 1;
402 break;
403 case 10:
404 vt->cur_enc = LAT1_MAP;
405 // fixme: dispaly controls = 0 ?
406 // fixme: toggle meta = 0 ?
407 break;
408 case 11:
409 vt->cur_enc = IBMPC_MAP;
410 // fixme: dispaly controls = 1 ?
411 // fixme: toggle meta = 0 ?
412 break;
413 case 12:
414 vt->cur_enc = IBMPC_MAP;
415 // fixme: dispaly controls = 1 ?
416 // fixme: toggle meta = 1 ?
417 break;
418 case 22:
419 vt->cur_attrib.bold = 0;
420 break;
421 case 24:
422 vt->cur_attrib.uline = 0;
423 break;
424 case 25:
425 vt->cur_attrib.blink = 0;
426 break;
427 case 27:
428 vt->cur_attrib.invers = 0;
429 break;
430 case 28:
431 vt->cur_attrib.unvisible = 0;
432 break;
433 case 30:
434 case 31:
435 case 32:
436 case 33:
437 case 34:
438 case 35:
439 case 36:
440 case 37:
441 /* set foreground color */
442 vt->cur_attrib.fgcol = color_table [vt->esc_buf[i] - 30];
443 break;
444 case 38:
445 /* reset color to default, enable underline */
446 vt->cur_attrib.fgcol = vt->default_attrib.fgcol;
447 vt->cur_attrib.uline = 1;
448 break;
449 case 39:
450 /* reset color to default, disable underline */
451 vt->cur_attrib.fgcol = vt->default_attrib.fgcol;
452 vt->cur_attrib.uline = 0;
453 break;
454 case 40:
455 case 41:
456 case 42:
457 case 43:
458 case 44:
459 case 45:
460 case 46:
461 case 47:
462 /* set background color */
463 vt->cur_attrib.bgcol = color_table [vt->esc_buf[i] - 40];
464 break;
465 case 49:
466 /* reset background color */
467 vt->cur_attrib.bgcol = vt->default_attrib.bgcol;
468 break;
469 default:
470 fprintf (stderr, "unhandled ESC[%d m code\n",vt->esc_buf[i]);
471 //fixme: implement
472 }
473 }
474}
475
476static void
d6a5f5a9 477spiceterm_save_cursor (spiceTerm *vt)
22e5ba02
DM
478{
479 vt->cx_saved = vt->cx;
480 vt->cy_saved = vt->cy;
481 vt->cur_attrib_saved = vt->cur_attrib;
482 vt->charset_saved = vt->charset;
483 vt->g0enc_saved = vt->g0enc;
484 vt->g1enc_saved = vt->g1enc;
485 vt->cur_enc_saved = vt->cur_enc;
486}
487
488static void
d6a5f5a9 489spiceterm_restore_cursor (spiceTerm *vt)
22e5ba02
DM
490{
491 vt->cx = vt->cx_saved;
492 vt->cy = vt->cy_saved;
493 vt->cur_attrib = vt->cur_attrib_saved;
494 vt->charset = vt->charset_saved;
495 vt->g0enc = vt->g0enc_saved;
496 vt->g1enc = vt->g1enc_saved;
497 vt->cur_enc = vt->cur_enc_saved;
498}
499
500static void
d6a5f5a9 501spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off)
22e5ba02
DM
502{
503 int x, y;
504
505 vt->y_displ = vt->y_base;
506
507 if (on_off) {
508
509 if (vt->altbuf) return;
510
511 vt->altbuf = 1;
512
513 /* alternate buffer & cursor */
514
d6a5f5a9 515 spiceterm_save_cursor (vt);
22e5ba02
DM
516 /* save screen to altcels */
517 for (y = 0; y < vt->height; y++) {
518 int y1 = (vt->y_base + y) % vt->total_height;
519 for (x = 0; x < vt->width; x++) {
520 vt->altcells[y*vt->width + x] = vt->cells[y1*vt->width + x];
521 }
522 }
523
524 /* clear screen */
525 for (y = 0; y <= vt->height; y++) {
526 for (x = 0; x < vt->width; x++) {
d6a5f5a9 527 spiceterm_clear_xy (vt, x, y);
22e5ba02
DM
528 }
529 }
530
531 } else {
532
533 if (vt->altbuf == 0) return;
534
535 vt->altbuf = 0;
536
537 /* restore saved data */
538 for (y = 0; y < vt->height; y++) {
539 int y1 = (vt->y_base + y) % vt->total_height;
540 for (x = 0; x < vt->width; x++) {
541 vt->cells[y1*vt->width + x] = vt->altcells[y*vt->width + x];
542 }
543 }
544
d6a5f5a9 545 spiceterm_restore_cursor (vt);
22e5ba02
DM
546 }
547
d6a5f5a9 548 spiceterm_refresh (vt);
22e5ba02
DM
549}
550
551static void
d6a5f5a9 552spiceterm_set_mode (spiceTerm *vt, int on_off)
22e5ba02
DM
553{
554 int i;
555
556 for (i = 0; i <= vt->esc_count; i++) {
557 if (vt->esc_ques) { /* DEC private modes set/reset */
558 switch(vt->esc_buf[i]) {
559 case 10: /* X11 mouse reporting on/off */
560 case 1000:
561 vt->report_mouse = on_off;
562 break;
563 case 1049: /* start/end special app mode (smcup/rmcup) */
d6a5f5a9 564 spiceterm_set_alternate_buffer (vt, on_off);
22e5ba02
DM
565 break;
566 case 25: /* Cursor on/off */
567 case 9: /* X10 mouse reporting on/off */
568 case 6: /* Origin relative/absolute */
569 case 1: /* Cursor keys in appl mode*/
570 case 5: /* Inverted screen on/off */
571 case 7: /* Autowrap on/off */
572 case 8: /* Autorepeat on/off */
573 break;
574 }
575 } else { /* ANSI modes set/reset */
576 /* fixme: implement me */
577 }
578 }
579}
580
581static void
d6a5f5a9 582spiceterm_gotoxy (spiceTerm *vt, int x, int y)
22e5ba02
DM
583{
584 /* verify all boundaries */
585
586 if (x < 0) {
587 x = 0;
588 }
589
590 if (x >= vt->width) {
591 x = vt->width - 1;
592 }
593
594 vt->cx = x;
595
596 if (y < 0) {
597 y = 0;
598 }
599
600 if (y >= vt->height) {
601 y = vt->height - 1;
602 }
603
604 vt->cy = y;
605}
606
607enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
608 EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
609 ESpalette, ESidquery, ESosc1, ESosc2};
610
611static void
d6a5f5a9 612spiceterm_putchar (spiceTerm *vt, unicode ch)
22e5ba02
DM
613{
614 int x, y, i, c;
615
616#ifdef DEBUG
617 if (!vt->tty_state)
618 fprintf (stderr, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy);
619#endif
620
621 switch(vt->tty_state) {
622 case ESesc:
623 vt->tty_state = ESnormal;
624 switch (ch) {
625 case '[':
626 vt->tty_state = ESsquare;
627 break;
628 case ']':
629 vt->tty_state = ESnonstd;
630 break;
631 case '%':
632 vt->tty_state = ESpercent;
633 break;
634 case '7':
d6a5f5a9 635 spiceterm_save_cursor (vt);
22e5ba02
DM
636 break;
637 case '8':
d6a5f5a9 638 spiceterm_restore_cursor (vt);
22e5ba02
DM
639 break;
640 case '(':
641 vt->tty_state = ESsetG0; // SET G0
642 break;
643 case ')':
644 vt->tty_state = ESsetG1; // SET G1
645 break;
646 case 'M':
647 /* cursor up (ri) */
648 if (vt->cy == vt->region_top)
d6a5f5a9 649 spiceterm_scroll_down (vt, vt->region_top, vt->region_bottom, 1);
22e5ba02
DM
650 else if (vt->cy > 0) {
651 vt->cy--;
652 }
653 break;
654 case '>':
655 /* numeric keypad - ignored */
656 break;
657 case '=':
658 /* appl. keypad - ignored */
659 break;
660 default:
661#ifdef DEBUG
662 fprintf(stderr, "got unhandled ESC%c %d\n", ch, ch);
663#endif
664 break;
665 }
666 break;
667 case ESnonstd: /* Operating System Controls */
668 vt->tty_state = ESnormal;
669
670 switch (ch) {
671 case 'P': /* palette escape sequence */
672 for(i = 0; i < MAX_ESC_PARAMS; i++) {
673 vt->esc_buf[i] = 0;
674 }
675
676 vt->esc_count = 0;
677 vt->tty_state = ESpalette;
678 break;
679 case 'R': /* reset palette */
680 // fixme: reset_palette(vc);
681 break;
682 case '0':
683 case '1':
684 case '2':
685 case '4':
686 vt->osc_cmd = ch;
687 vt->osc_textbuf[0] = 0;
688 vt->tty_state = ESosc1;
689 break;
690 default:
691#ifdef DEBUG
692 fprintf (stderr, "unhandled OSC %c\n", ch);
693#endif
694 vt->tty_state = ESnormal;
695 break;
696 }
697 break;
698 case ESosc1:
699 vt->tty_state = ESnormal;
700 if (ch == ';') {
701 vt->tty_state = ESosc2;
702 } else {
703#ifdef DEBUG
704 fprintf (stderr, "got illegal OSC sequence\n");
705#endif
706 }
707 break;
708 case ESosc2:
709 if (ch != 0x9c && ch != 7) {
710 int i = 0;
711 while (vt->osc_textbuf[i]) i++;
712 vt->osc_textbuf[i++] = ch;
713 vt->osc_textbuf[i] = 0;
714 } else {
715#ifdef DEBUG
716 fprintf (stderr, "OSC:%c:%s\n", vt->osc_cmd, vt->osc_textbuf);
717#endif
718 vt->tty_state = ESnormal;
719 }
720 break;
721 case ESpalette:
722 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
723 || (ch >= 'a' && ch <= 'f')) {
724 vt->esc_buf[vt->esc_count++] = (ch > '9' ? (ch & 0xDF) - 'A' + 10 : ch - '0');
725 if (vt->esc_count == 7) {
726 // fixme: this does not work - please test
727 /*
728 rfbColourMap *cmap =&vt->screen->colourMap;
729
730 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
731 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
732 cmap->data.bytes[i++] += vt->esc_buf[j++];
733 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
734 cmap->data.bytes[i++] += vt->esc_buf[j++];
735 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
736 cmap->data.bytes[i] += vt->esc_buf[j];
737 */
738 //set_palette(vc); ?
739
740 vt->tty_state = ESnormal;
741 }
742 } else
743 vt->tty_state = ESnormal;
744 break;
745 case ESsquare:
746 for(i = 0; i < MAX_ESC_PARAMS; i++) {
747 vt->esc_buf[i] = 0;
748 }
749
750 vt->esc_count = 0;
751 vt->esc_has_par = 0;
752 vt->tty_state = ESgetpars;
753
754 if (ch == '>') {
755 vt->tty_state = ESidquery;
756 break;
757 }
758
759 if ((vt->esc_ques = (ch == '?'))) {
760 break;
761 }
762 case ESgetpars:
763 if (ch >= '0' && ch <= '9') {
764 vt->esc_has_par = 1;
765 if (vt->esc_count < MAX_ESC_PARAMS) {
766 vt->esc_buf[vt->esc_count] = vt->esc_buf[vt->esc_count] * 10 + ch - '0';
767 }
768 break;
769 } else if (ch == ';') {
770 vt->esc_count++;
771 break;
772 } else {
773 if (vt->esc_has_par) {
774 vt->esc_count++;
775 }
776 vt->tty_state = ESgotpars;
777 }
778 case ESgotpars:
779
780 vt->tty_state = ESnormal;
781
782#ifdef DEBUG
783 char *qes = vt->esc_ques ? "?" : "";
784 if (vt->esc_count == 0) {
785 fprintf(stderr, "ESC[%s%c\n", qes, ch);
786 } else if (vt->esc_count == 1) {
787 fprintf(stderr, "ESC[%s%d%c\n", qes, vt->esc_buf[0], ch);
788 } else {
789 int i;
790 fprintf(stderr, "ESC[%s%d", qes, vt->esc_buf[0]);
791 for (i = 1; i < vt->esc_count; i++) {
792 fprintf(stderr, ";%d", vt->esc_buf[i]);
793 }
794 fprintf (stderr, "%c\n", ch);
795 }
796#endif
797
798 switch (ch) {
799 case 'h':
d6a5f5a9 800 spiceterm_set_mode (vt, 1);
22e5ba02
DM
801 break;
802 case 'l':
d6a5f5a9 803 spiceterm_set_mode (vt, 0);
22e5ba02
DM
804 break;
805 case 'm':
806 if (!vt->esc_count) {
807 vt->esc_count++; // default parameter 0
808 }
d6a5f5a9 809 spiceterm_csi_m (vt);
22e5ba02
DM
810 break;
811 case 'n':
812 /* report cursor position */
813 /* TODO: send ESC[row;colR */
814 break;
815 case 'A':
816 /* move cursor up */
817 if (vt->esc_buf[0] == 0) {
818 vt->esc_buf[0] = 1;
819 }
820 vt->cy -= vt->esc_buf[0];
821 if (vt->cy < 0) {
822 vt->cy = 0;
823 }
824 break;
825 case 'B':
826 case 'e':
827 /* move cursor down */
828 if (vt->esc_buf[0] == 0) {
829 vt->esc_buf[0] = 1;
830 }
831 vt->cy += vt->esc_buf[0];
832 if (vt->cy >= vt->height) {
833 vt->cy = vt->height - 1;
834 }
835 break;
836 case 'C':
837 case 'a':
838 /* move cursor right */
839 if (vt->esc_buf[0] == 0) {
840 vt->esc_buf[0] = 1;
841 }
842 vt->cx += vt->esc_buf[0];
843 if (vt->cx >= vt->width) {
844 vt->cx = vt->width - 1;
845 }
846 break;
847 case 'D':
848 /* move cursor left */
849 if (vt->esc_buf[0] == 0) {
850 vt->esc_buf[0] = 1;
851 }
852 vt->cx -= vt->esc_buf[0];
853 if (vt->cx < 0) {
854 vt->cx = 0;
855 }
856 break;
857 case 'G':
858 case '`':
859 /* move cursor to column */
d6a5f5a9 860 spiceterm_gotoxy (vt, vt->esc_buf[0] - 1, vt->cy);
22e5ba02
DM
861 break;
862 case 'd':
863 /* move cursor to row */
d6a5f5a9 864 spiceterm_gotoxy (vt, vt->cx , vt->esc_buf[0] - 1);
22e5ba02
DM
865 break;
866 case 'f':
867 case 'H':
868 /* move cursor to row, column */
d6a5f5a9 869 spiceterm_gotoxy (vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1);
22e5ba02
DM
870 break;
871 case 'J':
872 switch (vt->esc_buf[0]) {
873 case 0:
874 /* clear to end of screen */
875 for (y = vt->cy; y < vt->height; y++) {
876 for (x = 0; x < vt->width; x++) {
877 if (y == vt->cy && x < vt->cx) {
878 continue;
879 }
d6a5f5a9 880 spiceterm_clear_xy (vt, x, y);
22e5ba02
DM
881 }
882 }
883 break;
884 case 1:
885 /* clear from beginning of screen */
886 for (y = 0; y <= vt->cy; y++) {
887 for (x = 0; x < vt->width; x++) {
888 if (y == vt->cy && x > vt->cx) {
889 break;
890 }
d6a5f5a9 891 spiceterm_clear_xy (vt, x, y);
22e5ba02
DM
892 }
893 }
894 break;
895 case 2:
896 /* clear entire screen */
897 for (y = 0; y <= vt->height; y++) {
898 for (x = 0; x < vt->width; x++) {
d6a5f5a9 899 spiceterm_clear_xy (vt, x, y);
22e5ba02
DM
900 }
901 }
902 break;
903 }
904 break;
905 case 'K':
906 switch (vt->esc_buf[0]) {
907 case 0:
908 /* clear to eol */
909 for(x = vt->cx; x < vt->width; x++) {
d6a5f5a9 910 spiceterm_clear_xy (vt, x, vt->cy);
22e5ba02
DM
911 }
912 break;
913 case 1:
914 /* clear from beginning of line */
915 for (x = 0; x <= vt->cx; x++) {
d6a5f5a9 916 spiceterm_clear_xy (vt, x, vt->cy);
22e5ba02
DM
917 }
918 break;
919 case 2:
920 /* clear entire line */
921 for(x = 0; x < vt->width; x++) {
d6a5f5a9 922 spiceterm_clear_xy (vt, x, vt->cy);
22e5ba02
DM
923 }
924 break;
925 }
926 break;
927 case 'L':
928 /* insert line */
929 c = vt->esc_buf[0];
930
931 if (c > vt->height - vt->cy)
932 c = vt->height - vt->cy;
933 else if (!c)
934 c = 1;
935
d6a5f5a9 936 spiceterm_scroll_down (vt, vt->cy, vt->region_bottom, c);
22e5ba02
DM
937 break;
938 case 'M':
939 /* delete line */
940 c = vt->esc_buf[0];
941
942 if (c > vt->height - vt->cy)
943 c = vt->height - vt->cy;
944 else if (!c)
945 c = 1;
946
d6a5f5a9 947 spiceterm_scroll_up (vt, vt->cy, vt->region_bottom, c, 1);
22e5ba02
DM
948 break;
949 case 'T':
950 /* scroll down */
951 c = vt->esc_buf[0];
952 if (!c) c = 1;
d6a5f5a9 953 spiceterm_scroll_down (vt, vt->region_top, vt->region_bottom, c);
22e5ba02
DM
954 break;
955 case 'S':
956 /* scroll up */
957 c = vt->esc_buf[0];
958 if (!c) c = 1;
d6a5f5a9 959 spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, c, 1);
22e5ba02
DM
960 break;
961 case 'P':
962 /* delete c character */
963 c = vt->esc_buf[0];
964
965 if (c > vt->width - vt->cx)
966 c = vt->width - vt->cx;
967 else if (!c)
968 c = 1;
969
970 for (x = vt->cx; x < vt->width - c; x++) {
971 int y1 = (vt->y_base + vt->cy) % vt->total_height;
972 TextCell *dst = &vt->cells[y1 * vt->width + x];
973 TextCell *src = dst + c;
974 *dst = *src;
d6a5f5a9 975 spiceterm_update_xy (vt, x + c, vt->cy);
22e5ba02
DM
976 src->ch = ' ';
977 src->attrib = vt->default_attrib;
d6a5f5a9 978 spiceterm_update_xy (vt, x, vt->cy);
22e5ba02
DM
979 }
980 break;
981 case 's':
982 /* save cursor position */
d6a5f5a9 983 spiceterm_save_cursor (vt);
22e5ba02
DM
984 break;
985 case 'u':
986 /* restore cursor position */
d6a5f5a9 987 spiceterm_restore_cursor (vt);
22e5ba02
DM
988 break;
989 case 'X':
990 /* erase c characters */
991 c = vt->esc_buf[0];
992 if (!c) c = 1;
993
994 if (c > (vt->width - vt->cx)) c = vt->width - vt->cx;
995
996 for(i = 0; i < c; i++) {
d6a5f5a9 997 spiceterm_clear_xy (vt, vt->cx + i, vt->cy);
22e5ba02
DM
998 }
999 break;
1000 case '@':
1001 /* insert c character */
1002 c = vt->esc_buf[0];
1003 if (c > (vt->width - vt->cx)) {
1004 c = vt->width - vt->cx;
1005 }
1006 if (!c) c = 1;
1007
1008 for (x = vt->width - c; x >= vt->cx; x--) {
1009 int y1 = (vt->y_base + vt->cy) % vt->total_height;
1010 TextCell *src = &vt->cells[y1 * vt->width + x];
1011 TextCell *dst = src + c;
1012 *dst = *src;
d6a5f5a9 1013 spiceterm_update_xy (vt, x + c, vt->cy);
22e5ba02
DM
1014 src->ch = ' ';
1015 src->attrib = vt->cur_attrib;
d6a5f5a9 1016 spiceterm_update_xy (vt, x, vt->cy);
22e5ba02
DM
1017 }
1018
1019 break;
1020 case 'r':
1021 /* set region */
1022 if (!vt->esc_buf[0])
1023 vt->esc_buf[0]++;
1024 if (!vt->esc_buf[1])
1025 vt->esc_buf[1] = vt->height;
1026 /* Minimum allowed region is 2 lines */
1027 if (vt->esc_buf[0] < vt->esc_buf[1] &&
1028 vt->esc_buf[1] <= vt->height) {
1029 vt->region_top = vt->esc_buf[0] - 1;
1030 vt->region_bottom = vt->esc_buf[1];
1031 vt->cx = 0;
1032 vt->cy = vt->region_top;
1033#ifdef DEBUG
1034 fprintf (stderr, "set region %d %d\n", vt->region_top, vt->region_bottom);
1035#endif
1036 }
1037
1038 break;
1039 default:
1040#ifdef DEBUG
1041 if (vt->esc_count == 0) {
1042 fprintf(stderr, "unhandled escape ESC[%s%c\n", qes, ch);
1043 } else if (vt->esc_count == 1) {
1044 fprintf(stderr, "unhandled escape ESC[%s%d%c\n", qes, vt->esc_buf[0], ch);
1045 } else {
1046 int i;
1047 fprintf(stderr, "unhandled escape ESC[%s%d", qes, vt->esc_buf[0]);
1048 for (i = 1; i < vt->esc_count; i++) {
1049 fprintf(stderr, ";%d", vt->esc_buf[i]);
1050 }
1051 fprintf (stderr, "%c\n", ch);
1052 }
1053#endif
1054 break;
1055 }
1056 vt->esc_ques = 0;
1057 break;
1058 case ESsetG0: // Set G0
1059 vt->tty_state = ESnormal;
1060
1061 if (ch == '0')
1062 vt->g0enc = GRAF_MAP;
1063 else if (ch == 'B')
1064 vt->g0enc = LAT1_MAP;
1065 else if (ch == 'U')
1066 vt->g0enc = IBMPC_MAP;
1067 else if (ch == 'K')
1068 vt->g0enc = USER_MAP;
1069
1070 if (vt->charset == 0)
1071 vt->cur_enc = vt->g0enc;
1072
1073 break;
1074 case ESsetG1: // Set G1
1075 vt->tty_state = ESnormal;
1076
1077 if (ch == '0')
1078 vt->g1enc = GRAF_MAP;
1079 else if (ch == 'B')
1080 vt->g1enc = LAT1_MAP;
1081 else if (ch == 'U')
1082 vt->g1enc = IBMPC_MAP;
1083 else if (ch == 'K')
1084 vt->g1enc = USER_MAP;
1085
1086 if (vt->charset == 1)
1087 vt->cur_enc = vt->g1enc;
1088
1089 break;
1090 case ESidquery: // vt100 query id
1091 vt->tty_state = ESnormal;
1092
1093 if (ch == 'c') {
1094#ifdef DEBUG
1095 fprintf (stderr, "ESC[>c Query term ID\n");
1096#endif
d6a5f5a9 1097 spiceterm_respond_esc (vt, TERMIDCODE);
22e5ba02
DM
1098 }
1099 break;
1100 case ESpercent:
1101 vt->tty_state = ESnormal;
1102 switch (ch) {
1103 case '@': /* defined in ISO 2022 */
1104 vt->utf8 = 0;
1105 break;
1106 case 'G': /* prelim official escape code */
1107 case '8': /* retained for compatibility */
1108 vt->utf8 = 1;
1109 break;
1110 }
1111 break;
1112 default: // ESnormal
1113 vt->tty_state = ESnormal;
1114
1115 switch(ch) {
1116 case 0:
1117 break;
1118 case 7: /* alert aka. bell */
1119 // fixme:
3affb0e7 1120 //rfbSendBell(vt->screen);
22e5ba02
DM
1121 break;
1122 case 8: /* backspace */
1123 if (vt->cx > 0)
1124 vt->cx--;
1125 break;
1126 case 9: /* tabspace */
1127 if (vt->cx + (8 - (vt->cx % 8)) > vt->width) {
1128 vt->cx = 0;
d6a5f5a9 1129 spiceterm_put_lf (vt);
22e5ba02
DM
1130 } else {
1131 vt->cx = vt->cx + (8 - (vt->cx % 8));
1132 }
1133 break;
1134 case 10: /* LF,*/
1135 case 11: /* VT */
1136 case 12: /* FF */
d6a5f5a9 1137 spiceterm_put_lf (vt);
22e5ba02
DM
1138 break;
1139 case 13: /* carriage return */
1140 vt->cx = 0;
1141 break;
1142 case 14:
1143 /* SI (shift in), select character set 1 */
1144 vt->charset = 1;
1145 vt->cur_enc = vt->g1enc;
1146 /* fixme: display controls = 1 */
1147 break;
1148 case 15:
1149 /* SO (shift out), select character set 0 */
1150 vt->charset = 0;
1151 vt->cur_enc = vt->g0enc;
1152 /* fixme: display controls = 0 */
1153 break;
1154 case 27: /* esc */
1155 vt->tty_state = ESesc;
1156 break;
1157 case 127: /* delete */
1158 /* ignore */
1159 break;
1160 case 128+27: /* csi */
1161 vt->tty_state = ESsquare;
1162 break;
1163 default:
1164 if (vt->cx >= vt->width) {
1165 /* line wrap */
1166 vt->cx = 0;
d6a5f5a9 1167 spiceterm_put_lf (vt);
22e5ba02
DM
1168 }
1169
1170 int y1 = (vt->y_base + vt->cy) % vt->total_height;
1171 TextCell *c = &vt->cells[y1*vt->width + vt->cx];
1172 c->attrib = vt->cur_attrib;
1173 c->ch = ch;
d6a5f5a9 1174 spiceterm_update_xy (vt, vt->cx, vt->cy);
22e5ba02
DM
1175 vt->cx++;
1176 break;
1177 }
1178 break;
1179 }
1180}
1181
1182static int
d6a5f5a9 1183spiceterm_puts (spiceTerm *vt, const char *buf, int len)
22e5ba02
DM
1184{
1185 unicode tc;
1186
d6a5f5a9 1187 spiceterm_show_cursor (vt, 0);
22e5ba02
DM
1188
1189 while (len) {
1190 unsigned char c = *buf;
1191 len--;
1192 buf++;
1193
1194 if (vt->tty_state != ESnormal) {
1195 // never translate escape sequence
1196 tc = c;
1197 } else if (vt->utf8 && !vt->cur_enc) {
1198
1199 if(c & 0x80) { // utf8 multi-byte sequence
1200
1201 if (vt->utf_count > 0 && (c & 0xc0) == 0x80) {
1202 // inside UTF8 sequence
1203 vt->utf_char = (vt->utf_char << 6) | (c & 0x3f);
1204 vt->utf_count--;
1205 if (vt->utf_count == 0) {
1206 tc = vt->utf_char;
1207 } else {
1208 continue;
1209 }
1210 } else {
1211 // first char of a UTF8 sequence
1212 if ((c & 0xe0) == 0xc0) {
1213 vt->utf_count = 1;
1214 vt->utf_char = (c & 0x1f);
1215 } else if ((c & 0xf0) == 0xe0) {
1216 vt->utf_count = 2;
1217 vt->utf_char = (c & 0x0f);
1218 } else if ((c & 0xf8) == 0xf0) {
1219 vt->utf_count = 3;
1220 vt->utf_char = (c & 0x07);
1221 } else if ((c & 0xfc) == 0xf8) {
1222 vt->utf_count = 4;
1223 vt->utf_char = (c & 0x03);
1224 } else if ((c & 0xfe) == 0xfc) {
1225 vt->utf_count = 5;
1226 vt->utf_char = (c & 0x01);
1227 } else
1228 vt->utf_count = 0;
1229
1230 continue;
1231 }
1232 } else {
1233 // utf8 single byte
1234 tc = c;
1235 vt->utf_count = 0;
1236 }
1237
1238 } else {
1239 // never translate controls
1240 if (c >= 32 && c != 127 && c != (128+27)) {
1241 tc = translations[vt->cur_enc][c & 0x0ff];
1242 } else {
1243 tc = c;
1244 }
1245 }
1246
d6a5f5a9 1247 spiceterm_putchar (vt, tc);
22e5ba02
DM
1248 }
1249
d6a5f5a9 1250 spiceterm_show_cursor (vt, 1);
3affb0e7 1251
22e5ba02
DM
1252 return len;
1253}
1254
1255/* fixme:
1256void
d6a5f5a9 1257spiceterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
22e5ba02 1258{
d6a5f5a9 1259 spiceTerm *vt =(spiceTerm *)cl->screen->screenData;
22e5ba02
DM
1260
1261 // seems str is Latin-1 encoded
1262 if (vt->selection) free (vt->selection);
1263 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1264 int i;
1265 for (i = 0; i < len; i++) {
1266 vt->selection[i] = str[i] & 0xff;
1267 }
1268 vt->selection_len = len;
1269}
1270*/
abc13312 1271/*
22e5ba02 1272static void
d6a5f5a9 1273mouse_report (spiceTerm *vt, int butt, int mrx, int mry)
22e5ba02
DM
1274{
1275 char buf[8];
1276
1277 sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
1278 (char)('!' + mry));
1279
d6a5f5a9 1280 spiceterm_respond_esc (vt, buf);
22e5ba02 1281}
abc13312 1282*/
22e5ba02
DM
1283
1284void
d6a5f5a9 1285spiceterm_toggle_marked_cell (spiceTerm *vt, int pos)
22e5ba02 1286{
22e5ba02
DM
1287
1288/* fixme:
abc13312
DM
1289 int x= (pos%vt->width)*8;
1290 int y= (pos/vt->width)*16;
22e5ba02
DM
1291
1292 int i,j;
1293 rfbScreenInfoPtr s=vt->screen;
1294
1295 char *b = s->frameBuffer+y*s->width+x;
1296
1297 for (j=0; j < 16; j++) {
1298 for(i=0; i < 8; i++) {
1299 b[j*s->width+i] ^= 0x0f;
1300 rfbMarkRectAsModified (s, x, y, x+8, y+16);
1301 }
1302 }
1303*/
1304}
1305
1306/* fixme:
1307
1308void
d6a5f5a9 1309spiceterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
22e5ba02
DM
1310{
1311
d6a5f5a9 1312 spiceTerm *vt =(spiceTerm *)cl->screen->screenData;
22e5ba02
DM
1313 static int button2_released = 1;
1314 static int last_mask = 0;
1315 static int sel_start_pos = 0;
1316 static int sel_end_pos = 0;
1317 int i;
1318
1319 int cx = x/8;
1320 int cy = y/16;
1321
1322 if (cx < 0) cx = 0;
1323 if (cx >= vt->width) cx = vt->width - 1;
1324 if (cy < 0) cy = 0;
1325 if (cy >= vt->height) cy = vt->height - 1;
1326
1327 if (vt->report_mouse && buttonMask != last_mask) {
1328 last_mask = buttonMask;
1329 if (buttonMask & 1) {
1330 mouse_report (vt, 0, cx, cy);
1331 }
1332 if (buttonMask & 2) {
1333 mouse_report (vt, 1, cx, cy);
1334 }
1335 if (buttonMask & 4) {
1336 mouse_report (vt, 2, cx, cy);
1337 }
1338 if (!buttonMask) {
1339 mouse_report (vt, 3, cx, cy);
1340 }
1341 }
1342
1343 if (buttonMask & 2) {
1344 if(button2_released && vt->selection) {
1345 int i;
1346 for(i = 0; i < vt->selection_len; i++) {
1347 if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
1348 if (vt->utf8) {
1349 vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
1350 } else {
1351 vt->ibuf[vt->ibuf_count++] = vt->selection[i];
1352 }
1353 }
1354 }
1355 if (vt->y_displ != vt->y_base) {
1356 vt->y_displ = vt->y_base;
d6a5f5a9 1357 spiceterm_refresh (vt);
22e5ba02
DM
1358 }
1359 }
1360 button2_released = 0;
1361 } else {
1362 button2_released = 1;
1363 }
1364
1365 if (buttonMask & 1) {
1366 int pos = cy*vt->width + cx;
1367
d6a5f5a9 1368 // code borrowed from libspiceserver (SPICEonsole.c)
22e5ba02
DM
1369
1370 if (!vt->mark_active) {
1371
1372 vt->mark_active = 1;
1373 sel_start_pos = sel_end_pos = pos;
d6a5f5a9 1374 spiceterm_toggle_marked_cell (vt, pos);
22e5ba02
DM
1375
1376 } else {
1377
1378 if (pos != sel_end_pos) {
1379
1380 if (pos > sel_end_pos) {
1381 cx = sel_end_pos; cy=pos;
1382 } else {
1383 cx=pos; cy=sel_end_pos;
1384 }
1385
1386 if (cx < sel_start_pos) {
1387 if (cy < sel_start_pos) cy--;
1388 } else {
1389 cx++;
1390 }
1391
1392 while (cx <= cy) {
d6a5f5a9 1393 spiceterm_toggle_marked_cell (vt, cx);
22e5ba02
DM
1394 cx++;
1395 }
1396
1397 sel_end_pos = pos;
1398 }
1399 }
1400
1401 } else if (vt->mark_active) {
1402 vt->mark_active = 0;
1403
1404 if (sel_start_pos > sel_end_pos) {
1405 int tmp = sel_start_pos - 1;
1406 sel_start_pos = sel_end_pos;
1407 sel_end_pos = tmp;
1408 }
1409
1410 int len = sel_end_pos - sel_start_pos + 1;
1411
1412 if (vt->selection) free (vt->selection);
1413 vt->selection = (unicode *)malloc (len*sizeof (unicode));
1414 vt->selection_len = len;
1415 char *sel_latin1 = (char *)malloc (len + 1);
1416
1417 for (i = 0; i < len; i++) {
1418 int pos = sel_start_pos + i;
1419 int x = pos % vt->width;
1420 int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height;
1421 TextCell *c = &vt->cells[y1*vt->width + x];
1422 vt->selection[i] = c->ch;
1423 sel_latin1[i] = (char)c->ch;
1424 c++;
1425 }
1426 sel_latin1[len] = 0;
1427 rfbGotXCutText (vt->screen, sel_latin1, len);
1428 free (sel_latin1);
1429
1430 while (sel_start_pos <= sel_end_pos) {
d6a5f5a9 1431 spiceterm_toggle_marked_cell (vt, sel_start_pos++);
22e5ba02
DM
1432 }
1433
1434 }
1435
1436 rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
1437
1438}
1439*/
1440
22e5ba02
DM
1441static void my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
1442{
d6a5f5a9 1443 // spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
22e5ba02 1444
a1cf2514 1445 /* we no not need this */
3affb0e7 1446
a1cf2514 1447 return;
22e5ba02
DM
1448}
1449
c46a8251 1450static void my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags)
77133eee 1451{
d6a5f5a9 1452 spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
c46a8251
DM
1453 static int control = 0;
1454 static int shift = 0;
1455 char *esc = NULL;
1456
a1cf2514 1457 guint uc = 0;
c46a8251 1458
a1cf2514
DM
1459 fprintf (stderr, "KEYEVENT:%d: %08x\n", flags, keySym);fflush (stderr);
1460 if (flags & 1) {
c46a8251
DM
1461 if (keySym == GDK_KEY_Shift_L || keySym == GDK_KEY_Shift_R) {
1462 shift = 1;
1463 } if (keySym == GDK_KEY_Control_L || keySym == GDK_KEY_Control_R) {
1464 control = 1;
1465 } else if (vt->ibuf_count < (IBUFSIZE - 32)) {
1466
1467 if (control) {
1468 if(keySym >= 'a' && keySym <= 'z')
a1cf2514 1469 uc = keySym - 'a' + 1;
c46a8251 1470 else if (keySym >= 'A' && keySym <= 'Z')
a1cf2514 1471 uc = keySym - 'A' + 1;
c46a8251 1472 else
a1cf2514
DM
1473 uc = 0;
1474
1475
1476 //printf("CONTROL: %08x %d\n", keySym, uc);
1477
c46a8251
DM
1478 } else {
1479 switch (keySym) {
1480 case GDK_KEY_Escape:
a1cf2514 1481 uc = 27; break;
c46a8251 1482 case GDK_KEY_Return:
a1cf2514 1483 uc = '\r'; break;
c46a8251 1484 case GDK_KEY_BackSpace:
a1cf2514 1485 uc = 8; break;
c46a8251 1486 case GDK_KEY_Tab:
a1cf2514 1487 uc = '\t'; break;
c46a8251
DM
1488 case GDK_KEY_Delete: /* kdch1 */
1489 case GDK_KEY_KP_Delete:
1490 esc = "[3~";break;
1491 case GDK_KEY_Home: /* khome */
1492 case GDK_KEY_KP_Home:
1493 esc = "OH";break;
1494 case GDK_KEY_End:
1495 case GDK_KEY_KP_End: /* kend */
1496 esc = "OF";break;
1497 case GDK_KEY_Insert: /* kich1 */
1498 case GDK_KEY_KP_Insert:
1499 esc = "[2~";break;
1500 case GDK_KEY_Up:
1501 case GDK_KEY_KP_Up: /* kcuu1 */
1502 esc = "OA";break;
1503 case GDK_KEY_Down: /* kcud1 */
1504 case GDK_KEY_KP_Down:
1505 esc = "OB";break;
1506 case GDK_KEY_Right:
1507 case GDK_KEY_KP_Right: /* kcuf1 */
1508 esc = "OC";break;
1509 case GDK_KEY_Left:
1510 case GDK_KEY_KP_Left: /* kcub1 */
1511 esc = "OD";break;
1512 case GDK_KEY_Page_Up:
1513 if (shift) {
d6a5f5a9 1514 spiceterm_virtual_scroll (vt, -vt->height/2);
867d1eb5 1515 goto ret;
c46a8251
DM
1516 }
1517 esc = "[5~";break;
1518 case GDK_KEY_Page_Down:
1519 if (shift) {
d6a5f5a9 1520 spiceterm_virtual_scroll (vt, vt->height/2);
867d1eb5 1521 goto ret;
c46a8251
DM
1522 }
1523 esc = "[6~";break;
1524 case GDK_KEY_F1:
1525 esc = "OP";break;
1526 case GDK_KEY_F2:
1527 esc = "OQ";break;
1528 case GDK_KEY_F3:
1529 esc = "OR";break;
1530 case GDK_KEY_F4:
1531 esc = "OS";break;
1532 case GDK_KEY_F5:
1533 esc = "[15~";break;
1534 case GDK_KEY_F6:
1535 esc = "[17~";break;
1536 case GDK_KEY_F7:
1537 esc = "[18~";break;
1538 case GDK_KEY_F8:
1539 esc = "[19~";break;
1540 case GDK_KEY_F9:
1541 esc = "[20~";break;
1542 case GDK_KEY_F10:
1543 esc = "[21~";break;
1544 case GDK_KEY_F11:
1545 esc = "[23~";break;
1546 case GDK_KEY_F12:
1547 esc = "[24~";break;
1548 default:
a1cf2514
DM
1549 if (keySym < 0x100) {
1550 uc = keySym;
1551 }
c46a8251
DM
1552 break;
1553 }
1554 }
77133eee 1555
c46a8251 1556#ifdef DEBUG
a1cf2514 1557 fprintf(stderr, "KEYPRESS OUT:%s: %08x\n", esc, uc); fflush (stderr);
c46a8251 1558#endif
77133eee 1559
c46a8251
DM
1560 if (vt->y_displ != vt->y_base) {
1561 vt->y_displ = vt->y_base;
d6a5f5a9 1562 spiceterm_refresh (vt);
c46a8251
DM
1563 }
1564
1565 if (esc) {
d6a5f5a9 1566 spiceterm_respond_esc(vt, esc);
a1cf2514 1567 } else if (uc > 0) {
c46a8251
DM
1568 if (vt->utf8) {
1569 gchar buf[10];
a1cf2514 1570 gint len = g_unichar_to_utf8(uc, buf);
c46a8251
DM
1571
1572 if (len > 0) {
1573 int i;
1574 for (i = 0; i < len; i++) {
1575 vt->ibuf[vt->ibuf_count++] = buf[i];
1576 }
1577 }
1578 } else {
a1cf2514 1579 vt->ibuf[vt->ibuf_count++] = (char)uc;
c46a8251 1580 }
77133eee 1581 }
867d1eb5 1582 }
c46a8251
DM
1583 }
1584
a1cf2514
DM
1585
1586ret:
1587
c46a8251 1588 if (flags & 2) { // UP
a1cf2514
DM
1589 //printf("KEYRELEASE %08x\n", keySym);
1590
c46a8251
DM
1591 if (keySym == GDK_KEY_Shift_L || keySym == GDK_KEY_Shift_R) {
1592 shift = 0;
1593 } else if (keySym == GDK_KEY_Control_L || keySym == GDK_KEY_Control_R) {
1594 control = 0;
77133eee 1595 }
77133eee 1596 }
867d1eb5 1597
867d1eb5
DM
1598 vt->screen->core->watch_update_mask(vt->screen->mwatch,
1599 SPICE_WATCH_EVENT_READ|SPICE_WATCH_EVENT_WRITE);
77133eee
DM
1600}
1601
22e5ba02
DM
1602static uint8_t my_kbd_get_leds(SpiceKbdInstance *sin)
1603{
1604 return 0;
1605}
1606
1607static SpiceKbdInterface my_keyboard_sif = {
1608 .base.type = SPICE_INTERFACE_KEYBOARD ,
1609 .base.description = "spiceterm keyboard device",
1610 .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
1611 .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
c46a8251 1612 .push_keyval = my_kbd_push_keyval,
22e5ba02
DM
1613 .push_scan_freg = my_kbd_push_key,
1614 .get_leds = my_kbd_get_leds,
1615};
1616
d6a5f5a9
DM
1617spiceTerm *
1618create_spiceterm (int argc, char** argv, int maxx, int maxy)
22e5ba02
DM
1619{
1620 int i;
1621
64bc7a2f 1622 SpiceScreen *spice_screen;
22e5ba02
DM
1623
1624 SpiceCoreInterface *core = basic_event_loop_init();
64bc7a2f 1625 spice_screen = spice_screen_new(core);
22e5ba02 1626 //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
64bc7a2f
DM
1627 spice_screen_add_display_interface(spice_screen);
1628 spice_screen_add_agent_interface(spice_screen->server);
22e5ba02 1629
d6a5f5a9 1630 spiceTerm *vt = (spiceTerm *)calloc (sizeof(spiceTerm), 1);
3affb0e7 1631
22e5ba02 1632 vt->keyboard_sin.base.sif = &my_keyboard_sif.base;
64bc7a2f 1633 spice_server_add_interface(spice_screen->server, &vt->keyboard_sin.base);
22e5ba02 1634
64bc7a2f
DM
1635 // screen->setXCutText = spiceterm_set_xcut_text;
1636 // screen->ptrAddEvent = spiceterm_pointer_event;
1637 // screen->newClientHook = new_client;
1638 // screen->desktopName = "SPICE Command Terminal";
22e5ba02 1639
64bc7a2f
DM
1640 vt->maxx = spice_screen->width;
1641 vt->maxy = spice_screen->height;
22e5ba02
DM
1642
1643 vt->width = vt->maxx / 8;
1644 vt->height = vt->maxy / 16;
1645
1646 vt->total_height = vt->height * 20;
1647 vt->scroll_height = 0;
1648 vt->y_base = 0;
1649 vt->y_displ = 0;
1650
1651 vt->region_top = 0;
1652 vt->region_bottom = vt->height;
1653
1654 vt->g0enc = LAT1_MAP;
1655 vt->g1enc = GRAF_MAP;
1656 vt->cur_enc = vt->g0enc;
1657 vt->charset = 0;
1658
1659 /* default text attributes */
1660 vt->default_attrib.bold = 0;
1661 vt->default_attrib.uline = 0;
1662 vt->default_attrib.blink = 0;
1663 vt->default_attrib.invers = 0;
1664 vt->default_attrib.unvisible = 0;
1665 vt->default_attrib.fgcol = 7;
1666 vt->default_attrib.bgcol = 0;
1667
1668 vt->cur_attrib = vt->default_attrib;
1669
1670 vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height);
1671
1672 for (i = 0; i < vt->width*vt->total_height; i++) {
1673 vt->cells[i].ch = ' ';
1674 vt->cells[i].attrib = vt->default_attrib;
1675 }
1676
1677 vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height);
1678
64bc7a2f 1679 vt->screen = spice_screen;
22e5ba02
DM
1680
1681 return vt;
1682}
1683
1684static void master_watch(int master, int event, void *opaque)
1685{
d6a5f5a9 1686 spiceTerm *vt = (spiceTerm *)opaque;
22e5ba02
DM
1687
1688 printf("CHANNEL EVENT %d\n", event);
1689
1690 // fixme: if (!vt->mark_active) {
1691
1692 if (event == SPICE_WATCH_EVENT_READ) {
1693 char buffer[1024];
1694 int c;
1695 while ((c = read(master, buffer, 1024)) == -1) {
1696 if (errno != EAGAIN) break;
1697 }
1698 if (c == -1) {
1699 g_error("got read error"); // fixme
1700 }
d6a5f5a9 1701 spiceterm_puts (vt, buffer, c);
22e5ba02
DM
1702 } else {
1703 if (vt->ibuf_count > 0) {
77133eee 1704 printf ("DEBUG: WRITE %x %d\n", vt->ibuf[0], vt->ibuf_count);
22e5ba02 1705 write (master, vt->ibuf, vt->ibuf_count);
3affb0e7 1706 vt->ibuf_count = 0; // fixme: what if not all data written
22e5ba02
DM
1707 }
1708 vt->screen->core->watch_update_mask(vt->screen->mwatch, SPICE_WATCH_EVENT_READ);
1709 }
1710}
1711
1712int
1713main (int argc, char** argv)
1714{
1715 int i;
1716 char **cmdargv = NULL;
1717 char *command = "/bin/bash"; // execute normal shell as default
1718 int pid;
1719 int master;
1720 char ptyname[1024];
22e5ba02
DM
1721 struct winsize dimensions;
1722
63a34f43
DM
1723 g_thread_init(NULL);
1724
22e5ba02
DM
1725 for (i = 1; i < argc; i++) {
1726 if (!strcmp (argv[i], "-c")) {
1727 command = argv[i+1];
1728 cmdargv = &argv[i+1];
1729 argc = i;
1730 argv[i] = NULL;
1731 break;
1732 }
1733 }
1734
abc13312
DM
1735 if (0) print_usage(NULL); // fixme:
1736
d6a5f5a9 1737 spiceTerm *vt = create_spiceterm (argc, argv, 745, 400);
22e5ba02
DM
1738
1739 setlocale(LC_ALL, ""); // set from environment
1740
1741 char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE
1742
1743 // fixme: ist there a standard way to detect utf8 mode ?
1744 if (strcasestr (ctype, ".utf-8")||strcasestr (ctype, ".utf8")) {
1745 vt->utf8 = 1;
1746 }
1747
1748 dimensions.ws_col = vt->width;
1749 dimensions.ws_row = vt->height;
1750
1751 setenv ("TERM", TERM, 1);
1752
3affb0e7 1753 printf("EXEC: %s\n", command);
22e5ba02
DM
1754
1755 pid = forkpty (&master, ptyname, NULL, &dimensions);
1756 if(!pid) {
1757
1758 // install default signal handlers
1759 signal (SIGQUIT, SIG_DFL);
1760 signal (SIGTERM, SIG_DFL);
1761 signal (SIGINT, SIG_DFL);
1762
1763 if (cmdargv) {
1764 execvp (command, cmdargv);
1765 } else {
1766 execlp (command, command, NULL);
1767 }
1768 perror ("Error: exec failed\n");
1769 exit (-1); // should not be reached
1770 } else if (pid == -1) {
1771 perror ("Error: fork failed\n");
1772 exit (-1);
1773 }
1774
1775
1776 vt->screen->mwatch = vt->screen->core->watch_add(
3affb0e7 1777 master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */,
22e5ba02
DM
1778 master_watch, vt);
1779
1780 basic_event_loop_mainloop();
1781
1782 //rfbProcessEvents (vt->screen, 40000); /* 40 ms */
1783
1784 /*
1785 if (vt->ibuf_count > 0) {
1786 printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
1787 write (master, vt->ibuf, vt->ibuf_count);
1788 vt->ibuf_count = 0;
1789 last_time = time (NULL);
1790 }
1791 */
1792
1793 kill (pid, 9);
1794 int status;
1795 waitpid(pid, &status, 0);
1796
1797 exit (0);
1798}