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