1 /*
2 * A Z-Machine
3 * Copyright (C) 2000 Andrew Hunter
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /*
21 * Version 6 display
22 */
23
24 /*
25 * Note to porters: In order to support v6, you'll need to support
26 * the 'Pixmap display' functions; they aren't used for other display
27 * styles. The v6 display code itself is actually device independant.
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "zmachine.h"
35 #include "display.h"
36 #include "format.h"
37 #include "v6display.h"
38
39 typedef struct v6window v6window;
40
41 static int active_win = 0;
42
43 static int erf_n, erf_d;
44
45 static int mouse_win;
46
47 struct v6window
48 {
49 float curx, cury;
50
51 int xpos, ypos;
52 int width, height;
53
54 int lmargin, rmargin;
55 int fore, back;
56
57 int style;
58
59 float line_height;
60
61 float text_amount;
62 int no_scroll;
63 int force_fixed;
64 int no_more;
65
66 int wrapping;
67
68 int want_more;
69 };
70
71 static v6window win[8];
72
73 static int (*nl_func)(const int * remaining,
74 int rem_len) = NULL;
75
76 #define ACTWIN win[active_win]
77
v6_startup(void)78 void v6_startup (void)
79 {
80 #ifdef DEBUG
81 printf_debug("V6: startup\n");
82 #endif
83
84 if (!display_init_pixmap(-1, -1))
85 {
86 zmachine_fatal("Display driver does not support pixmap display: unable to initialise v6 display");
87 return;
88 }
89 display_is_v6();
90
91 machine.dinfo = display_get_info();
92
93 v6_reset();
94
95 machine.memory[ZH_width] = machine.dinfo->width>>8;
96 machine.memory[ZH_width+1] = machine.dinfo->width;
97 machine.memory[ZH_height] = machine.dinfo->height>>8;
98 machine.memory[ZH_height+1] = machine.dinfo->height;
99 /* Note that these are backwards in v6 :-) */
100 machine.memory[ZH_fontwidth] = machine.dinfo->font_height;
101 machine.memory[ZH_fontheight] = machine.dinfo->font_width;
102 }
103
v6_reset(void)104 void v6_reset (void)
105 {
106 int x;
107
108 #ifdef DEBUG
109 printf_debug("V6: reset\n");
110 #endif
111
112 v6_reset_windows();
113
114 for (x=0; x<8; x++)
115 {
116 win[x].fore = DEFAULT_FORE;
117 win[x].back = DEFAULT_BACK;
118 }
119
120 v6_set_window(0);
121 v6_erase_window();
122
123 erf_n = 1; erf_d = 1;
124
125 if (machine.blorb != NULL && machine.blorb->reso.offset != -1)
126 {
127 if (machine.dinfo->width*machine.blorb->reso.py >
128 machine.dinfo->height*machine.blorb->reso.px)
129 {
130 erf_n = machine.dinfo->height;
131 erf_d = machine.blorb->reso.py;
132 }
133 else
134 {
135 erf_n = machine.dinfo->width;
136 erf_d = machine.blorb->reso.px;
137 }
138 }
139 }
140
v6_scale_image(BlorbImage * img,int * img_n,int * img_d)141 void v6_scale_image(BlorbImage* img, int* img_n, int* img_d)
142 {
143 *img_n = erf_n*img->std_n;
144 *img_d = erf_d;
145
146 if ((*img_n * img->max_d) > (img->max_n * *img_d))
147 {
148 *img_n = img->max_n;
149 *img_d = img->max_d;
150 }
151 else if ((*img_n * img->min_d) < (img->min_n * *img_d))
152 {
153 *img_n = img->min_n;
154 *img_d = img->min_d;
155 }
156 }
157
v6_reset_windows(void)158 void v6_reset_windows(void)
159 {
160 int x;
161 #ifdef DEBUG
162 printf_debug("V6: reset windows\n");
163 #endif
164
165 for (x=0; x<8; x++)
166 {
167 win[x].curx = win[x].cury = 0;
168 win[x].xpos = win[x].ypos = 0;
169 win[x].width = machine.dinfo->width;
170 win[x].height = machine.dinfo->height;
171 win[x].style = 0;
172 win[x].line_height = display_get_font_height(0);
173 win[x].force_fixed = 0;
174 win[x].text_amount = 0;
175 win[x].no_scroll = 0;
176 win[x].no_more = 0;
177 win[x].want_more = 0;
178 win[x].wrapping = 1;
179 }
180 }
181
scroll_to_height(int height,int change_baseline)182 static void scroll_to_height(int height, int change_baseline)
183 {
184 int oldheight, bg;
185
186 bg = ACTWIN.back;
187
188 oldheight = ACTWIN.line_height;
189 if (height > oldheight)
190 {
191 ACTWIN.line_height = height;
192 }
193
194 /* Scroll the line to fit in a bigger font, if necessary */
195 if ((ACTWIN.cury+ACTWIN.line_height) > (ACTWIN.ypos+ACTWIN.height) &&
196 !ACTWIN.no_scroll)
197 {
198 int scrollby;
199
200 scrollby = (ACTWIN.cury+ACTWIN.line_height)-(ACTWIN.ypos+ACTWIN.height);
201 #ifdef DEBUG
202 printf_debug("V6: Scrolling by %i\n", scrollby);
203 #endif
204
205 display_scroll_region(ACTWIN.xpos, ACTWIN.ypos+scrollby,
206 ACTWIN.width, ACTWIN.height,
207 0, -scrollby);
208 if (bg >= 0)
209 {
210 display_pixmap_cols(bg, 0);
211 display_plot_rect(ACTWIN.xpos+ACTWIN.lmargin,
212 ACTWIN.ypos+ACTWIN.height-scrollby,
213 ACTWIN.width-ACTWIN.lmargin-ACTWIN.rmargin,
214 scrollby);
215 }
216
217 ACTWIN.cury -= scrollby;
218
219 #ifdef DEBUG
220 printf_debug("V6: Text amount is now %g (more paging %s)\n",
221 ACTWIN.text_amount, ACTWIN.no_more?"OFF":"ON");
222 #endif
223 }
224
225 if (!change_baseline)
226 return;
227
228 /*
229 * Need to scroll any existing text on this line down, so
230 * baselines match
231 */
232 if (height > oldheight && !ACTWIN.no_scroll && oldheight > 0)
233 {
234 int scrollby;
235
236 scrollby = height-oldheight;
237
238 display_scroll_region(ACTWIN.xpos, ACTWIN.cury,
239 ACTWIN.width, oldheight,
240 0, scrollby);
241
242 if (bg >= 0)
243 {
244 display_pixmap_cols(bg, 0);
245 display_plot_rect(ACTWIN.xpos+ACTWIN.lmargin, ACTWIN.cury,
246 ACTWIN.width-ACTWIN.lmargin-ACTWIN.rmargin,
247 scrollby);
248 }
249 }
250 }
251
v6_prints(const int * text)252 void v6_prints(const int* text)
253 {
254 int height;
255 int start_pos, text_pos, last_word, this_word;
256 float width;
257
258 int fg, bg;
259 int len;
260
261 #ifdef DEBUG
262 char t[8192];
263
264 for (len=0; text[len] !=0; len++)
265 t[len] = text[len];
266 t[len] = 0;
267
268 printf_debug("V6: Printing text to window %i (style %x, colours %i, %i, position %g, %g): >%s<\n", active_win,
269 ACTWIN.style, ACTWIN.fore, ACTWIN.back, ACTWIN.curx, ACTWIN.cury, t);
270 #endif
271
272 for (len=0; text[len] != 0; len++);
273
274 fg = ACTWIN.fore;
275 bg = ACTWIN.back;
276
277 start_pos = text_pos = last_word = this_word = 0;
278 width = 0;
279
280 while (text[text_pos] != 0)
281 {
282 /* On to the next newline */
283 start_pos = text_pos;
284 width = 0;
285 last_word = this_word = text_pos;
286
287 while (ACTWIN.curx + width <= ACTWIN.xpos + ACTWIN.width - ACTWIN.rmargin &&
288 text[text_pos] != 10 && text[text_pos] != 0)
289 {
290 if (text[text_pos] == ' ' ||
291 text[text_pos] == '-' ||
292 ACTWIN.wrapping == 0)
293 {
294 /* Possible break point */
295 width = display_measure_text(text + start_pos,
296 text_pos - start_pos + 1,
297 ACTWIN.style);
298
299 last_word = this_word;
300 this_word = text_pos+1;
301 }
302 text_pos++;
303 }
304
305 if (text[text_pos] == 0 || text[text_pos] == 10)
306 width = display_measure_text(text + start_pos,
307 text_pos - start_pos,
308 ACTWIN.style);
309
310 /* Back up a word, if necessary */
311 if (ACTWIN.curx + width >= ACTWIN.xpos + ACTWIN.width - ACTWIN.rmargin &&
312 (ACTWIN.curx != ACTWIN.xpos + ACTWIN.lmargin || last_word != start_pos))
313 {
314 text_pos = last_word;
315 this_word = last_word;
316
317 width = display_measure_text(text + start_pos,
318 text_pos - start_pos,
319 ACTWIN.style);
320 }
321
322 /* Work out the new height of this line... */
323 if (text_pos != start_pos ||
324 (text_pos == start_pos && text[text_pos] == 10))
325 height = display_get_font_height(ACTWIN.style);
326 else
327 height = ACTWIN.line_height;
328
329 scroll_to_height(height, 1);
330
331 /* Plot the text */
332 display_pixmap_cols(fg, bg);
333
334 display_plot_gtext(text + start_pos, text_pos - start_pos,
335 ACTWIN.style,
336 (ACTWIN.curx+0.5),
337 0.5+ACTWIN.cury+ACTWIN.line_height-display_get_font_descent(ACTWIN.style));
338
339 #ifdef DEBUG
340 { int x;
341 printf_debug("V6: printed line >");
342 for (x=start_pos; x<text_pos; x++)
343 printf_debug("%c", text[x]);
344 printf_debug("<\n");
345 }
346 #endif
347
348 /* Stop plotting if wrapping is off */
349 if (ACTWIN.wrapping == 0)
350 {
351 ACTWIN.curx += width;
352 return;
353 }
354
355 /* Newline, if necessary */
356 if (text[text_pos] != 0)
357 {
358 int more;
359
360 #ifdef DEBUG
361 printf_debug("V6: new line\n");
362 #endif
363
364 ACTWIN.text_amount += ACTWIN.line_height;
365 ACTWIN.cury += ACTWIN.line_height;
366 ACTWIN.line_height = 0;
367 ACTWIN.curx = ACTWIN.xpos + ACTWIN.lmargin;
368
369 if (ACTWIN.text_amount + ACTWIN.line_height > ACTWIN.height -
370 (ACTWIN.line_height+display_get_font_height(ACTWIN.style)))
371 {
372 if (!ACTWIN.no_more)
373 ACTWIN.want_more = 1;
374 }
375
376 more = -1;
377 if (nl_func != NULL)
378 {
379 more = (nl_func)(text + text_pos + (text[text_pos]==10?1:0),
380 len - text_pos - (text[text_pos]==10?1:0));
381 }
382
383 if (more == 1 && !ACTWIN.no_more)
384 {
385 display_wait_for_more();
386 ACTWIN.text_amount = ACTWIN.line_height*2;
387 }
388 else if (more == -1 && ACTWIN.want_more)
389 {
390 ACTWIN.text_amount = ACTWIN.line_height*2;
391 display_wait_for_more();
392 }
393 ACTWIN.want_more = 0;
394
395 scroll_to_height(display_get_font_height(ACTWIN.style), 0);
396
397 if (more == 2)
398 return;
399 }
400 else
401 {
402 ACTWIN.curx += width;
403 }
404
405 if (text[text_pos] == 10 || text[text_pos] == 32)
406 text_pos++;
407 }
408 }
409
v6_prints_c(const char * text)410 void v6_prints_c(const char* text)
411 {
412 #ifdef DEBUG
413 printf_debug("YAARRRK\n");
414 #endif
415 }
416
v6_set_caret(void)417 void v6_set_caret(void)
418 {
419 display_set_input_pos(ACTWIN.style, ACTWIN.curx, ACTWIN.cury,
420 (ACTWIN.xpos+ACTWIN.width-ACTWIN.rmargin)-ACTWIN.curx);
421 if (ACTWIN.style&1)
422 display_pixmap_cols(ACTWIN.back, ACTWIN.fore);
423 else
424 display_pixmap_cols(ACTWIN.fore, ACTWIN.back);
425
426 if (mouse_win >= 0)
427 {
428 display_set_mouse_win(win[mouse_win].xpos, win[mouse_win].ypos,
429 win[mouse_win].width, win[mouse_win].height);
430 }
431 else
432 {
433 display_set_mouse_win(-1, -1, -1, -1);
434 }
435
436 ACTWIN.text_amount = 0;
437 }
438
v6_erase_window(void)439 void v6_erase_window(void)
440 {
441 #ifdef DEBUG
442 printf_debug("V6: erase window #%i\n", active_win);
443 #endif
444
445 if (ACTWIN.style&1)
446 display_pixmap_cols(ACTWIN.fore, ACTWIN.back);
447 else
448 display_pixmap_cols(ACTWIN.back, ACTWIN.fore);
449
450 display_plot_rect(ACTWIN.xpos, ACTWIN.ypos,
451 ACTWIN.width, ACTWIN.height);
452
453 ACTWIN.cury = ACTWIN.ypos;
454 ACTWIN.curx = ACTWIN.xpos + ACTWIN.lmargin;
455 ACTWIN.line_height = ACTWIN.text_amount = display_get_font_height(ACTWIN.style);
456 ACTWIN.text_amount *= 2;
457 }
458
v6_erase_line(int val)459 void v6_erase_line(int val)
460 {
461 #ifdef DEBUG
462 printf_debug("V6: erase line : %i %g\n", val, ACTWIN.curx);
463 #endif
464
465 if (ACTWIN.line_height == 0)
466 {
467 scroll_to_height(display_get_font_height(ACTWIN.style), 1);
468 ACTWIN.line_height = display_get_font_height(ACTWIN.style);
469 }
470
471 if (ACTWIN.style&1)
472 display_pixmap_cols(ACTWIN.fore, 0);
473 else
474 display_pixmap_cols(ACTWIN.back, 0);
475 if (val == 1)
476 display_plot_rect(ACTWIN.curx, ACTWIN.cury,
477 ACTWIN.width-ACTWIN.rmargin-(ACTWIN.curx-ACTWIN.xpos),
478 ACTWIN.line_height);
479 else
480 {
481 if (ACTWIN.curx + val > ACTWIN.xpos + ACTWIN.width - ACTWIN.rmargin)
482 {
483 val = ACTWIN.width - ACTWIN.rmargin - ACTWIN.curx;
484 }
485
486 display_plot_rect(ACTWIN.curx, ACTWIN.cury,
487 val, ACTWIN.line_height);
488 }
489 }
490
v6_set_colours(int fg,int bg)491 void v6_set_colours(int fg, int bg)
492 {
493 #ifdef DEBUG
494 printf_debug("V6: set colours: %i, %i (window %i)\n", fg, bg, active_win);
495 #endif
496
497 if (fg == -2)
498 fg = ACTWIN.fore;
499 if (bg == -2)
500 bg = ACTWIN.back;
501 if (fg == -1)
502 fg = DEFAULT_FORE;
503 if (bg == -1)
504 bg = DEFAULT_BACK;
505
506 if (bg == -3)
507 bg = display_get_pix_colour(ACTWIN.curx, ACTWIN.cury);
508 if (fg == -3)
509 fg = display_get_pix_colour(ACTWIN.curx, ACTWIN.cury);
510
511 ACTWIN.fore = fg;
512 ACTWIN.back = bg;
513 }
514
v6_get_fg_colour(void)515 int v6_get_fg_colour(void)
516 {
517 return ACTWIN.fore;
518 }
519
v6_get_bg_colour(void)520 int v6_get_bg_colour(void)
521 {
522 return ACTWIN.back;
523 }
524
v6_set_style(int style)525 int v6_set_style(int style)
526 {
527 int oldstyle;
528
529 oldstyle = ACTWIN.style;
530
531 if (style == 0)
532 ACTWIN.style = 0;
533 else
534 {
535 if (style > 0)
536 ACTWIN.style |= style;
537 else
538 ACTWIN.style &= ~(-style);
539 }
540
541 #ifdef DEBUG
542 printf_debug("V6: set style (window %i), new style %x\n", active_win,
543 ACTWIN.style);
544 #endif
545
546 return oldstyle;
547 }
548
v6_get_window(void)549 int v6_get_window(void)
550 {
551 return active_win;
552 }
553
v6_set_window(int window)554 void v6_set_window(int window)
555 {
556 if (window < 0 || window >= 8)
557 {
558 zmachine_fatal("Attempt to use nonexistent window %i", window);
559 }
560
561 #ifdef DEBUG
562 printf_debug("V6: window set to %i\n", window);
563 #endif
564 active_win = window;
565 }
566
v6_define_window(int window,int x,int y,int lmargin,int rmargin,int width,int height)567 void v6_define_window(int window,
568 int x, int y,
569 int lmargin, int rmargin,
570 int width, int height)
571 {
572 #ifdef DEBUG
573 printf_debug("V6: defining window %i - at (%i, %i), size %ix%i, margins %i, %i\n", window,
574 x, y, width, height, lmargin, rmargin);
575 #endif
576
577 x--; y--;
578
579 win[window].xpos = x;
580 win[window].ypos = y;
581 win[window].lmargin = lmargin;
582 win[window].rmargin = rmargin;
583 win[window].width = width;
584 win[window].height = height;
585
586 if (win[window].curx < win[window].xpos + win[window].lmargin)
587 win[window].curx = win[window].xpos + win[window].lmargin;
588
589 if (win[window].cury < win[window].ypos)
590 {
591 win[window].cury = win[window].ypos;
592 }
593
594 if (win[window].cury+win[window].line_height > win[window].ypos + win[window].height)
595 {
596 win[window].line_height = display_get_font_height(win[window].style);
597 win[window].cury = win[window].ypos + win[window].height - win[window].line_height;
598
599 if (!ACTWIN.no_scroll)
600 scroll_to_height(display_get_font_height(ACTWIN.style), 0);
601 }
602
603 win[window].text_amount = win[window].line_height*2;
604 }
605
v6_set_scroll(int flag)606 void v6_set_scroll(int flag)
607 {
608 ACTWIN.no_scroll = !flag;
609 }
610
v6_set_more(int window,int flag)611 void v6_set_more(int window, int flag)
612 {
613 win[window].no_more = !flag;
614 }
615
v6_set_wrap(int window,int flag)616 void v6_set_wrap(int window, int flag)
617 {
618 win[window].wrapping = flag;
619 }
620
v6_set_cursor(int x,int y)621 void v6_set_cursor(int x, int y)
622 {
623 int ly;
624
625 #ifdef DEBUG
626 printf_debug("V6: moving cursor to %i, %i\n", x, y);
627 #endif
628
629 ly = ACTWIN.cury;
630
631 x--; y--;
632 ACTWIN.curx = ACTWIN.xpos + x;
633 ACTWIN.cury = ACTWIN.ypos + y;
634
635 if (ly != ACTWIN.cury)
636 ACTWIN.line_height = display_get_font_height(ACTWIN.style);
637
638 ACTWIN.text_amount = 0;
639 }
640
v6_get_cursor_x(void)641 int v6_get_cursor_x(void)
642 {
643 return ACTWIN.curx-ACTWIN.xpos+1;
644 }
645
v6_get_cursor_y(void)646 int v6_get_cursor_y(void)
647 {
648 return ACTWIN.cury-ACTWIN.ypos+1;
649 }
650
v6_set_newline_function(int (* func)(const int * remaining,int rem_len))651 void v6_set_newline_function(int (*func)(const int * remaining,
652 int rem_len))
653 {
654 nl_func = func;
655 }
656
v6_scroll_window(int window,int amount)657 void v6_scroll_window(int window, int amount)
658 {
659 #ifdef DEBUG
660 printf_debug("Scrolling window %i by %i\n", window, amount);
661 #endif
662
663 if (amount > 0)
664 {
665 display_scroll_region(win[window].xpos, win[window].ypos+amount,
666 win[window].width, win[window].height-amount,
667 0, -amount);
668
669 if (win[window].back >= 0)
670 {
671 display_pixmap_cols(win[window].back, 0);
672 display_plot_rect(win[window].xpos,
673 win[window].ypos+win[window].height-amount,
674 win[window].width, amount);
675 }
676 }
677 else
678 {
679 display_scroll_region(win[window].xpos, win[window].ypos,
680 win[window].width, win[window].height+amount,
681 0, -amount);
682
683 if (win[window].back >= 0)
684 {
685 display_pixmap_cols(win[window].back, 0);
686 display_plot_rect(win[window].xpos, win[window].ypos,
687 win[window].width, -amount);
688 }
689 }
690 }
691
v6_split_point(int * text,int text_len,int width,int * width_out)692 int v6_split_point(int* text,
693 int text_len,
694 int width,
695 int* width_out)
696 {
697 float cwidth;
698 int text_pos;
699 int this_word, last_word;
700
701 this_word = last_word = text_pos = 0;
702 cwidth = 0;
703
704 while (cwidth < width &&
705 text_pos < text_len)
706 {
707 if (text[text_pos] == ' ' ||
708 text[text_pos] == '-')
709 {
710 cwidth = display_measure_text(text,
711 text_pos,
712 ACTWIN.style);
713
714 last_word = this_word;
715 this_word = text_pos + 1;
716 }
717 text_pos++;
718 }
719
720 cwidth = display_measure_text(text,
721 text_pos,
722 ACTWIN.style);
723 if (width_out != NULL)
724 *width_out = cwidth;
725
726 if (cwidth >= width)
727 return last_word;
728
729 return text_len;
730 }
731
v6_measure_text(int * text,int len)732 int v6_measure_text(int* text,
733 int len)
734 {
735 return display_measure_text(text,
736 len,
737 ACTWIN.style);
738 }
739
v6_set_mouse_win(int win)740 void v6_set_mouse_win(int win)
741 {
742 mouse_win = win;
743 }
744