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