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