fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
at master 863 lines 16 kB view raw
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}