fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/drivers/video/terminal.c *
7 * Created: 2003-04-18 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2003-2025 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <config.h>
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <drivers/video/terminal.h>
30#include <lib/sysdep.h>
31
32
33#define TRM_ESC_ESC 1
34#define TRM_ESC_OK 2
35#define TRM_ESC_ALT 4
36#define TRM_ESC_CTRL 8
37#define TRM_ESC_SUPER 16
38#if defined(PCE_HOST_MACOS)
39#define TRM_ESC_KEYS (TRM_ESC_SUPER | TRM_ESC_CTRL)
40#else
41#define TRM_ESC_KEYS (TRM_ESC_ALT | TRM_ESC_CTRL)
42#endif
43
44
45void trm_init (terminal_t *trm, void *ext)
46{
47 trm->ext = ext;
48
49 trm->set_msg_emu_ext = NULL;
50 trm->set_msg_emu = NULL;
51
52 trm->set_key_ext = NULL;
53 trm->set_key = NULL;
54
55 trm->set_mouse_ext = NULL;
56 trm->set_mouse = NULL;
57
58 trm->get_mouse_ext = NULL;
59 trm->get_mouse = NULL;
60
61 trm->del = NULL;
62 trm->set_msg_trm = NULL;
63 trm->update = NULL;
64 trm->check = NULL;
65
66 trm->is_open = 0;
67
68 trm->escape_key = PCE_KEY_ESC;
69 trm->escape = 0;
70
71 trm->w = 0;
72 trm->h = 0;
73
74 trm->buf_cnt = 0;
75 trm->buf = NULL;
76
77 trm->scale = 1;
78
79 trm->aspect_x = 4;
80 trm->aspect_y = 3;
81
82 trm->min_w = 320;
83 trm->min_h = 240;
84
85 trm->mouse_scale_x[0] = 1;
86 trm->mouse_scale_x[1] = 1;
87 trm->mouse_scale_x[2] = 0;
88 trm->mouse_scale_y[0] = 1;
89 trm->mouse_scale_y[1] = 1;
90 trm->mouse_scale_y[2] = 0;
91
92 trm->scale_buf_cnt = 0;
93 trm->scale_buf = NULL;
94
95 trm->update_x = 0;
96 trm->update_y = 0;
97 trm->update_w = 0;
98 trm->update_h = 0;
99
100 trm->pict_index = 0;
101}
102
103void trm_free (terminal_t *trm)
104{
105 trm_close (trm);
106
107 free (trm->buf);
108 free (trm->scale_buf);
109}
110
111void trm_del (terminal_t *trm)
112{
113 if (trm == NULL) {
114 return;
115 }
116
117 trm_free (trm);
118
119 if (trm->del != NULL) {
120 trm->del (trm->ext);
121 }
122}
123
124int trm_open (terminal_t *trm, unsigned w, unsigned h)
125{
126 if (trm->is_open) {
127 return (0);
128 }
129
130 trm_set_size (trm, w, h);
131
132 if (trm->open != NULL) {
133 if (trm->open (trm->ext, w, h)) {
134 return (1);
135 }
136 }
137
138 trm->is_open = 1;
139
140 return (0);
141}
142
143int trm_close (terminal_t *trm)
144{
145 if (trm->is_open == 0) {
146 return (0);
147 }
148
149 if (trm->close != NULL) {
150 if (trm->close (trm->ext)) {
151 return (1);
152 }
153 }
154
155 trm->is_open = 0;
156
157 return (0);
158}
159
160void trm_set_msg_fct (terminal_t *trm, void *ext, void *fct)
161{
162 trm->set_msg_emu_ext = ext;
163 trm->set_msg_emu = fct;
164}
165
166void trm_set_key_fct (terminal_t *trm, void *ext, void *fct)
167{
168 trm->set_key_ext = ext;
169 trm->set_key = fct;
170}
171
172void trm_set_mouse_fct (terminal_t *trm, void *ext, void *fct)
173{
174 trm->set_mouse_ext = ext;
175 trm->set_mouse = fct;
176}
177
178void trm_set_get_mouse_fct (terminal_t *trm, void *ext, void *fct)
179{
180 trm->get_mouse_ext = ext;
181 trm->get_mouse = fct;
182}
183
184static
185int trm_screenshot_get_name (terminal_t *trm, char *str)
186{
187 unsigned i;
188
189 for (i = 0; i < 256; i++) {
190 sprintf (str, "pce%04u.ppm", trm->pict_index++);
191
192 if (pce_file_exists (str) == 0) {
193 return (0);
194 }
195 }
196
197 return (1);
198}
199
200int trm_screenshot (terminal_t *trm, const char *fname)
201{
202 FILE *fp;
203 char str[256];
204 unsigned long cnt;
205
206 if ((fname == NULL) || (fname[0] == 0)) {
207 if (trm_screenshot_get_name (trm, str)) {
208 return (1);
209 }
210
211 fname = str;
212 }
213
214 if ((fp = fopen (fname, "wb")) == NULL) {
215 return (1);
216 }
217
218 cnt = 3 * (unsigned long) trm->w * (unsigned long) trm->h;
219
220 fprintf (fp, "P6\n%u %u\n%u\x0a", trm->w, trm->h, 255);
221
222 if (fwrite (trm->buf, 1, cnt, fp) != cnt) {
223 fclose (fp);
224 return (1);
225 }
226
227 fclose (fp);
228
229 return (0);
230}
231
232int trm_set_msg_trm (terminal_t *trm, const char *msg, const char *val)
233{
234 if (strcmp (msg, "term.escape") == 0) {
235 trm_set_escape_str (trm, val);
236 return (0);
237 }
238
239 if (strcmp (msg, "term.screenshot") == 0) {
240 trm_screenshot (trm, val);
241 return (0);
242 }
243
244 if (trm->set_msg_trm != NULL) {
245 return (trm->set_msg_trm (trm->ext, msg, val));
246 }
247
248 return (-1);
249}
250
251void trm_set_mouse_scale (terminal_t *trm, int mul_x, int div_x, int mul_y, int div_y)
252{
253 trm->mouse_scale_x[0] = mul_x;
254 trm->mouse_scale_x[1] = div_x;
255 trm->mouse_scale_x[2] = 0;
256
257 trm->mouse_scale_y[0] = mul_y;
258 trm->mouse_scale_y[1] = div_y;
259 trm->mouse_scale_y[2] = 0;
260}
261
262void trm_set_size (terminal_t *trm, unsigned w, unsigned h)
263{
264 unsigned long cnt;
265
266 if ((w == 0) || (h == 0)) {
267 free (trm->buf);
268
269 trm->buf_cnt = 0;
270 trm->buf = NULL;
271
272 trm->w = 0;
273 trm->h = 0;
274
275 return;
276 }
277
278 if ((trm->w == w) && (trm->h == h)) {
279 return;
280 }
281
282 cnt = 3 * (unsigned long) w * (unsigned long) h;
283
284 if (trm->buf_cnt != cnt) {
285 unsigned char *tmp;
286
287 tmp = realloc (trm->buf, cnt);
288 if (tmp == NULL) {
289 return;
290 }
291
292 trm->buf = tmp;
293 trm->buf_cnt = cnt;
294 }
295
296 trm->w = w;
297 trm->h = h;
298
299 trm->update_x = 0;
300 trm->update_y = 0;
301 trm->update_w = w;
302 trm->update_h = h;
303}
304
305void trm_set_min_size (terminal_t *trm, unsigned w, unsigned h)
306{
307 trm->min_w = w;
308 trm->min_h = h;
309}
310
311void trm_set_scale (terminal_t *trm, unsigned v)
312{
313 trm->scale = (v < 1) ? 1 : v;
314
315 trm->update_x = 0;
316 trm->update_y = 0;
317 trm->update_w = trm->w;
318 trm->update_h = trm->h;
319}
320
321void trm_set_aspect_ratio (terminal_t *trm, unsigned x, unsigned y)
322{
323 trm->aspect_x = x;
324 trm->aspect_y = y;
325}
326
327void trm_set_pixel (terminal_t *trm, unsigned x, unsigned y, const unsigned char *col)
328{
329 unsigned char *buf;
330
331 buf = trm->buf + 3 * (trm->w * y + x);
332
333 buf[0] = col[0];
334 buf[1] = col[1];
335 buf[2] = col[2];
336
337 if (trm->update_w == 0) {
338 trm->update_x = x;
339 trm->update_w = 1;
340 }
341 else {
342 if (x < trm->update_x) {
343 trm->update_w += trm->update_x - x;
344 trm->update_x = x;
345 }
346
347 if (x >= (trm->update_x + trm->update_w)) {
348 trm->update_w = x - trm->update_x + 1;
349 }
350 }
351
352 if (trm->update_h == 0) {
353 trm->update_y = y;
354 trm->update_h = 1;
355 }
356 else {
357 if (y < trm->update_y) {
358 trm->update_h += trm->update_y - y;
359 trm->update_y = y;
360 }
361
362 if (y >= (trm->update_y + trm->update_h)) {
363 trm->update_h = y - trm->update_y + 1;
364 }
365 }
366}
367
368void trm_set_lines (terminal_t *trm, const void *buf, unsigned y, unsigned cnt)
369{
370 unsigned long w3, tmp;
371 const unsigned char *src;
372 unsigned char *dst;
373
374 w3 = 3UL * trm->w;
375
376 src = buf;
377 dst = trm->buf + w3 * y;
378
379 while (cnt > 0) {
380 if (memcmp (dst, src, w3) != 0) {
381 break;
382 }
383
384 src += w3;
385 dst += w3;
386 y += 1;
387 cnt -= 1;
388 }
389
390 tmp = cnt * w3;
391
392 while (cnt > 0) {
393 tmp -= w3;
394
395 if (memcmp (dst + tmp, src + tmp, w3) != 0) {
396 break;
397 }
398
399 cnt -= 1;
400 }
401
402 if (cnt == 0) {
403 return;
404 }
405
406 memcpy (dst, src, w3 * cnt);
407
408 trm->update_x = 0;
409 trm->update_w = trm->w;
410
411 if (trm->update_h == 0) {
412 trm->update_y = y;
413 trm->update_h = cnt;
414 }
415 else {
416 if (y < trm->update_y) {
417 trm->update_h += trm->update_y - y;
418 trm->update_y = y;
419 }
420
421 if ((y + cnt) > (trm->update_y + trm->update_h)) {
422 trm->update_h = (y + cnt) - trm->update_y;
423 }
424 }
425}
426
427void trm_update (terminal_t *trm)
428{
429 if ((trm->update_w == 0) || (trm->update_h == 0)) {
430 return;
431 }
432
433 if (trm->update != NULL) {
434 trm->update (trm->ext);
435 }
436
437 trm->update_x = 0;
438 trm->update_y = 0;
439 trm->update_w = 0;
440 trm->update_h = 0;
441}
442
443void trm_check (terminal_t *trm)
444{
445 if (trm->check != NULL) {
446 trm->check (trm->ext);
447 }
448}
449
450
451int trm_set_msg_emu (terminal_t *trm, const char *msg, const char *val)
452{
453 if (trm->set_msg_emu != NULL) {
454 return (trm->set_msg_emu (trm->set_msg_emu_ext, msg, val));
455 }
456
457 return (1);
458}
459
460int trm_set_escape_str (terminal_t *trm, const char *str)
461{
462 pce_key_t key;
463
464 key = pce_key_from_string (str);
465
466 if (key == PCE_KEY_NONE) {
467 return (1);
468 }
469
470 trm->escape_key = key;
471
472 return (0);
473}
474
475void trm_set_escape_key (terminal_t *trm, pce_key_t key)
476{
477 trm->escape_key = key;
478}
479
480static
481int trm_set_key_magic (terminal_t *trm, pce_key_t key)
482{
483 switch (key) {
484 case PCE_KEY_0:
485 trm_set_msg_emu (trm, "emu.cpu.speed", "0");
486 return (0);
487
488 case PCE_KEY_1:
489 trm_set_msg_emu (trm, "emu.cpu.speed", "1");
490 return (0);
491
492 case PCE_KEY_2:
493 trm_set_msg_emu (trm, "emu.cpu.speed", "2");
494 return (0);
495
496 case PCE_KEY_3:
497 trm_set_msg_emu (trm, "emu.cpu.speed", "3");
498 return (0);
499
500 case PCE_KEY_4:
501 trm_set_msg_emu (trm, "emu.cpu.speed", "4");
502 return (0);
503
504 case PCE_KEY_5:
505 trm_set_msg_emu (trm, "emu.cpu.speed", "5");
506 return (0);
507
508 case PCE_KEY_6:
509 trm_set_msg_emu (trm, "emu.cpu.speed", "6");
510 return (0);
511
512 case PCE_KEY_7:
513 trm_set_msg_emu (trm, "emu.cpu.speed", "7");
514 return (0);
515
516 case PCE_KEY_8:
517 trm_set_msg_emu (trm, "emu.cpu.speed", "8");
518 return (0);
519
520
521 case PCE_KEY_A:
522 trm_set_msg_trm (trm, "term.autosize", "");
523 return (0);
524
525 case PCE_KEY_F:
526 trm_set_msg_trm (trm, "term.fullscreen.toggle", "");
527 return (0);
528
529 case PCE_KEY_G:
530 trm_set_msg_trm (trm, "term.grab", "");
531 return (0);
532
533 case PCE_KEY_M:
534 trm_set_msg_trm (trm, "term.release", "");
535 trm_set_msg_trm (trm, "term.fullscreen", "0");
536 trm_set_msg_emu (trm, "emu.stop", "1");
537 return (0);
538
539 case PCE_KEY_P:
540 trm_set_msg_emu (trm, "emu.pause.toggle", "");
541 return (0);
542
543 case PCE_KEY_Q:
544 trm_set_msg_trm (trm, "term.release", "");
545 trm_set_msg_trm (trm, "term.fullscreen", "0");
546 trm_set_msg_emu (trm, "emu.exit", "1");
547 return (0);
548
549 case PCE_KEY_R:
550 trm_set_msg_emu (trm, "emu.reset", "1");
551 return (0);
552
553 case PCE_KEY_S:
554 trm_screenshot (trm, NULL);
555 return (0);
556
557 case PCE_KEY_UP:
558 case PCE_KEY_KP_8:
559 trm_set_scale (trm, trm->scale + 1);
560 return (0);
561
562 case PCE_KEY_DOWN:
563 case PCE_KEY_KP_2:
564 if (trm->scale > 1) {
565 trm_set_scale (trm, trm->scale - 1);
566 }
567 return (0);
568
569 case PCE_KEY_LEFT:
570 case PCE_KEY_KP_4:
571 trm_set_msg_emu (trm, "emu.cpu.speed.step", "-1");
572 return (0);
573
574 case PCE_KEY_RIGHT:
575 case PCE_KEY_KP_6:
576 trm_set_msg_emu (trm, "emu.cpu.speed.step", "+1");
577 return (0);
578
579 default:
580 return (1);
581 }
582
583 return (1);
584}
585
586static
587void trm_set_key_emu (terminal_t *trm, unsigned event, pce_key_t key)
588{
589 if (event == PCE_KEY_EVENT_MAGIC) {
590 if (trm_set_key_magic (trm, key) == 0) {
591 return;
592 }
593 }
594
595 if (trm->set_key != NULL) {
596 trm->set_key (trm->set_key_ext, event, key);
597 }
598}
599
600void trm_set_key (terminal_t *trm, unsigned event, pce_key_t key)
601{
602 if (event == PCE_KEY_EVENT_DOWN) {
603 if (key == trm->escape_key) {
604 trm->escape ^= TRM_ESC_ESC;
605 }
606
607 if (trm->escape & TRM_ESC_ESC) {
608 return;
609 }
610
611 if (key == PCE_KEY_LALT || key == PCE_KEY_RALT) {
612 trm->escape |= TRM_ESC_ALT;
613 }
614 else if (key == PCE_KEY_LSUPER || key == PCE_KEY_RSUPER) {
615 trm->escape |= TRM_ESC_SUPER;
616 }
617 else if (key == PCE_KEY_LCTRL || key == PCE_KEY_RCTRL) {
618 trm->escape |= TRM_ESC_CTRL;
619 }
620 else if (key == PCE_KEY_PAUSE) {
621 if ((trm->escape & TRM_ESC_KEYS) == 0) {
622 trm_set_msg_trm (trm, "term.release", "");
623 trm_set_msg_trm (trm, "term.fullscreen", "0");
624 trm_set_msg_emu (trm, "emu.exit", "1");
625 return;
626 }
627 }
628
629 if ((trm->escape & TRM_ESC_KEYS) == TRM_ESC_KEYS) {
630 trm_set_msg_trm (trm, "term.release", "");
631 trm->escape &= ~TRM_ESC_KEYS;
632 }
633 }
634 else if (event == PCE_KEY_EVENT_UP) {
635 if (trm->escape & TRM_ESC_ESC) {
636 if (key != trm->escape_key) {
637 trm_set_key_emu (trm, PCE_KEY_EVENT_MAGIC, key);
638 trm->escape &= ~TRM_ESC_ESC;
639 }
640 return;
641 }
642
643 if (key == PCE_KEY_LALT || key == PCE_KEY_RALT) {
644 trm->escape &= ~TRM_ESC_ALT;
645 }
646 else if (key == PCE_KEY_LSUPER || key == PCE_KEY_RSUPER) {
647 trm->escape &= ~TRM_ESC_SUPER;
648 }
649 else if (key == PCE_KEY_LCTRL || key == PCE_KEY_RCTRL) {
650 trm->escape &= ~TRM_ESC_CTRL;
651 }
652 else if (key == PCE_KEY_PAUSE) {
653 return;
654 }
655 }
656
657 trm_set_key_emu (trm, event, key);
658}
659
660void trm_set_mouse (terminal_t *trm, int dx, int dy, unsigned but)
661{
662 int tx, ty;
663
664 dx = trm->mouse_scale_x[0] * dx + trm->mouse_scale_x[2];
665 tx = dx;
666 dx = dx / trm->mouse_scale_x[1];
667 trm->mouse_scale_x[2] = tx - trm->mouse_scale_x[1] * dx;
668
669 dy = trm->mouse_scale_y[0] * dy + trm->mouse_scale_y[2];
670 ty = dy;
671 dy = dy / trm->mouse_scale_y[1];
672 trm->mouse_scale_y[2] = ty - trm->mouse_scale_y[1] * dy;
673
674 if (trm->set_mouse != NULL) {
675 trm->set_mouse (trm->set_mouse_ext, dx, dy, but);
676 }
677}
678
679void trm_get_mouse (terminal_t *trm, int *x, int *y)
680{
681 if (trm->get_mouse != NULL) {
682 trm->get_mouse (trm->get_mouse_ext, x, y);
683 }
684 else {
685 *x = 0;
686 *y = 0;
687 }
688}
689
690void trm_get_scale (terminal_t *trm, unsigned w, unsigned h, unsigned *fx, unsigned *fy)
691{
692 unsigned long f;
693 unsigned long w2, h2;
694 unsigned long maxw, maxh;
695
696 if ((w == 0) || (h == 0)) {
697 *fx = 0;
698 *fy = 0;
699 return;
700 }
701
702 f = 1;
703
704 while (((f * w) < trm->min_w) && ((f * h) < trm->min_h)) {
705 f += 1;
706 }
707
708 *fx = trm->scale + f - 1;
709 *fy = trm->scale + f - 1;
710
711 if ((trm->aspect_x > 0) && (trm->aspect_y > 0)) {
712 w2 = *fx * w;
713 h2 = *fy * h;
714
715 maxw = (trm->aspect_x * h2) / trm->aspect_y;
716 maxh = (trm->aspect_y * w2) / trm->aspect_x;
717
718 while ((h2 + h) <= maxh) {
719 h2 += h;
720 *fy += 1;
721 }
722
723 while ((w2 + w) <= maxw) {
724 w2 += w;
725 *fx += 1;
726 }
727
728 if (w2 < maxw) {
729 if ((trm->aspect_y * (w2 + w - maxw)) < (trm->aspect_x * (maxw - w2))) {
730 w2 += w;
731 *fx += 1;
732 }
733 }
734 }
735}
736
737static
738unsigned char *trm_scale_get_buf (terminal_t *trm, unsigned w, unsigned h)
739{
740 unsigned long cnt;
741 unsigned char *tmp;
742
743 cnt = 3 * (unsigned long) w * (unsigned long) h;
744
745 if (cnt > trm->scale_buf_cnt) {
746 tmp = realloc (trm->scale_buf, cnt);
747 if (tmp == NULL) {
748 return (NULL);
749 }
750
751 trm->scale_buf = tmp;
752 trm->scale_buf_cnt = cnt;
753 }
754
755 return (trm->scale_buf);
756}
757
758static
759void trm_scale_w1 (unsigned char *dst, const unsigned char *src, unsigned w, unsigned h, unsigned fy)
760{
761 unsigned i, n, y;
762
763 n = 3 * w;
764
765 for (y = 0; y < h; y++) {
766 memcpy (dst, src, n);
767
768 dst += n;
769 src += n;
770
771 for (i = 1; i < fy; i++) {
772 memcpy (dst, dst - n, n);
773 dst += n;
774 }
775 }
776}
777
778static
779void trm_scale_w2 (unsigned char *dst, const unsigned char *src, unsigned w, unsigned h, unsigned fy)
780{
781 unsigned i, n, x, y;
782
783 n = 6 * w;
784
785 for (y = 0; y < h; y++) {
786 for (x = 0; x < w; x++) {
787 dst[0] = src[0];
788 dst[1] = src[1];
789 dst[2] = src[2];
790 dst[3] = src[0];
791 dst[4] = src[1];
792 dst[5] = src[2];
793
794 dst += 6;
795 src += 3;
796 }
797
798 for (i = 1; i < fy; i++) {
799 memcpy (dst, dst - n, n);
800 dst += n;
801 }
802 }
803}
804
805static
806void trm_scale_wx (unsigned char *dst, const unsigned char *src, unsigned w, unsigned h, unsigned fx, unsigned fy)
807{
808 unsigned i, j, n, x, y;
809
810 n = 3 * fx * w;
811
812 for (y = 0; y < h; y++) {
813 for (x = 0; x < w; x++) {
814 for (i = 0; i < fx; i++) {
815 dst[0] = src[0];
816 dst[1] = src[1];
817 dst[2] = src[2];
818
819 dst += 3;
820 }
821
822 src += 3;
823 }
824
825 for (j = 1; j < fy; j++) {
826 memcpy (dst, dst - n, n);
827 dst += n;
828 }
829 }
830}
831
832const unsigned char *trm_scale (terminal_t *trm,
833 const unsigned char *src, unsigned w, unsigned h,
834 unsigned fx, unsigned fy)
835{
836 unsigned dw, dh;
837 unsigned char *dst;
838
839 if ((fx == 1) && (fy == 1)) {
840 return (src);
841 }
842
843 dw = fx * w;
844 dh = fy * h;
845
846 dst = trm_scale_get_buf (trm, dw, dh);
847
848 if (dst == NULL) {
849 return (NULL);
850 }
851
852 if (fx == 1) {
853 trm_scale_w1 (dst, src, w, h, fy);
854 }
855 else if (fx == 2) {
856 trm_scale_w2 (dst, src, w, h, fy);
857 }
858 else {
859 trm_scale_wx (dst, src, w, h, fx, fy);
860 }
861
862 return (dst);
863}