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