1 /* sdldisplay.c: Routines for dealing with the SDL display
2    Copyright (c) 2000-2006 Philip Kendall, Matan Ziv-Av, Fredrick Meunier
3    Copyright (c) 2015 Adrien Destugues
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program 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
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License along
16    with this program; if not, write to the Free Software Foundation, Inc.,
17    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 
19    Author contact information:
20 
21    E-mail: philip-fuse@shadowmagic.org.uk
22 
23 */
24 
25 #include <config.h>
26 
27 #include <limits.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <SDL.h>
31 
32 #include <libspectrum.h>
33 
34 #include "display.h"
35 #include "fuse.h"
36 #include "machine.h"
37 #include "peripherals/scld.h"
38 #include "screenshot.h"
39 #include "settings.h"
40 #include "ui/ui.h"
41 #include "ui/scaler/scaler.h"
42 #include "ui/uidisplay.h"
43 #include "utils.h"
44 
45 SDL_Surface *sdldisplay_gc = NULL;   /* Hardware screen */
46 static SDL_Surface *tmp_screen=NULL; /* Temporary screen for scalers */
47 
48 static SDL_Surface *red_cassette[2], *green_cassette[2];
49 static SDL_Surface *red_mdr[2], *green_mdr[2];
50 static SDL_Surface *red_disk[2], *green_disk[2];
51 
52 static ui_statusbar_state sdl_disk_state, sdl_mdr_state, sdl_tape_state;
53 static int sdl_status_updated;
54 
55 static int tmp_screen_width;
56 
57 static Uint32 colour_values[16];
58 
59 static SDL_Color colour_palette[] = {
60   {   0,   0,   0,   0 },
61   {   0,   0, 192,   0 },
62   { 192,   0,   0,   0 },
63   { 192,   0, 192,   0 },
64   {   0, 192,   0,   0 },
65   {   0, 192, 192,   0 },
66   { 192, 192,   0,   0 },
67   { 192, 192, 192,   0 },
68   {   0,   0,   0,   0 },
69   {   0,   0, 255,   0 },
70   { 255,   0,   0,   0 },
71   { 255,   0, 255,   0 },
72   {   0, 255,   0,   0 },
73   {   0, 255, 255,   0 },
74   { 255, 255,   0,   0 },
75   { 255, 255, 255,   0 }
76 };
77 
78 static Uint32 bw_values[16];
79 
80 /* This is a rule of thumb for the maximum number of rects that can be updated
81    each frame. If more are generated we just update the whole screen */
82 #define MAX_UPDATE_RECT 300
83 static SDL_Rect updated_rects[MAX_UPDATE_RECT];
84 static int num_rects = 0;
85 static libspectrum_byte sdldisplay_force_full_refresh = 1;
86 
87 static int max_fullscreen_height;
88 static int min_fullscreen_height;
89 static int fullscreen_width = 0;
90 static int fullscreen_x_off = 0;
91 static int fullscreen_y_off = 0;
92 
93 /* The current size of the display (in units of DISPLAY_SCREEN_*) */
94 static float sdldisplay_current_size = 1;
95 
96 static libspectrum_byte sdldisplay_is_full_screen = 0;
97 
98 static int image_width;
99 static int image_height;
100 
101 static int timex;
102 
103 static void init_scalers( void );
104 static int sdldisplay_allocate_colours( int numColours, Uint32 *colour_values,
105                                         Uint32 *bw_values );
106 
107 static int sdldisplay_load_gfx_mode( void );
108 
109 static void
init_scalers(void)110 init_scalers( void )
111 {
112   scaler_register_clear();
113 
114   scaler_register( SCALER_NORMAL );
115   scaler_register( SCALER_2XSAI );
116   scaler_register( SCALER_SUPER2XSAI );
117   scaler_register( SCALER_SUPEREAGLE );
118   scaler_register( SCALER_ADVMAME2X );
119   scaler_register( SCALER_ADVMAME3X );
120   scaler_register( SCALER_DOTMATRIX );
121   scaler_register( SCALER_PALTV );
122   scaler_register( SCALER_HQ2X );
123   if( machine_current->timex ) {
124     scaler_register( SCALER_HALF );
125     scaler_register( SCALER_HALFSKIP );
126     scaler_register( SCALER_TIMEXTV );
127     scaler_register( SCALER_TIMEX1_5X );
128     scaler_register( SCALER_TIMEX2X );
129   } else {
130     scaler_register( SCALER_DOUBLESIZE );
131     scaler_register( SCALER_TRIPLESIZE );
132     scaler_register( SCALER_QUADSIZE );
133     scaler_register( SCALER_TV2X );
134     scaler_register( SCALER_TV3X );
135     scaler_register( SCALER_TV4X );
136     scaler_register( SCALER_PALTV2X );
137     scaler_register( SCALER_PALTV3X );
138     scaler_register( SCALER_PALTV4X );
139     scaler_register( SCALER_HQ3X );
140     scaler_register( SCALER_HQ4X );
141   }
142 
143   if( scaler_is_supported( current_scaler ) ) {
144     scaler_select_scaler( current_scaler );
145   } else {
146     scaler_select_scaler( SCALER_NORMAL );
147   }
148 }
149 
150 static int
sdl_convert_icon(SDL_Surface * source,SDL_Surface ** icon,int red)151 sdl_convert_icon( SDL_Surface *source, SDL_Surface **icon, int red )
152 {
153   SDL_Surface *copy;   /* Copy with altered palette */
154   int i;
155 
156   SDL_Color colors[ source->format->palette->ncolors ];
157 
158   copy = SDL_ConvertSurface( source, source->format, SDL_SWSURFACE );
159 
160   for( i = 0; i < copy->format->palette->ncolors; i++ ) {
161     colors[i].r = red ? copy->format->palette->colors[i].r : 0;
162     colors[i].g = red ? 0 : copy->format->palette->colors[i].g;
163     colors[i].b = 0;
164   }
165 
166   SDL_SetPalette( copy, SDL_LOGPAL, colors, 0, i );
167 
168   icon[0] = SDL_ConvertSurface( copy, tmp_screen->format, SDL_SWSURFACE );
169 
170   SDL_FreeSurface( copy );
171 
172   icon[1] = SDL_CreateRGBSurface( SDL_SWSURFACE,
173                                   (icon[0]->w)<<1, (icon[0]->h)<<1,
174                                   icon[0]->format->BitsPerPixel,
175                                   icon[0]->format->Rmask,
176                                   icon[0]->format->Gmask,
177                                   icon[0]->format->Bmask,
178                                   icon[0]->format->Amask
179                                 );
180 
181   ( scaler_get_proc16( SCALER_DOUBLESIZE ) )(
182         (libspectrum_byte*)icon[0]->pixels,
183         icon[0]->pitch,
184         (libspectrum_byte*)icon[1]->pixels,
185         icon[1]->pitch, icon[0]->w, icon[0]->h
186       );
187 
188   return 0;
189 }
190 
191 static int
sdl_load_status_icon(const char * filename,SDL_Surface ** red,SDL_Surface ** green)192 sdl_load_status_icon( const char*filename, SDL_Surface **red, SDL_Surface **green )
193 {
194   char path[ PATH_MAX ];
195   SDL_Surface *temp;    /* Copy of image as loaded */
196 
197   if( utils_find_file_path( filename, path, UTILS_AUXILIARY_LIB ) ) {
198     fprintf( stderr, "%s: Error getting path for icons\n", fuse_progname );
199     return -1;
200   }
201 
202   if((temp = SDL_LoadBMP(path)) == NULL) {
203     fprintf( stderr, "%s: Error loading icon \"%s\" text:%s\n", fuse_progname,
204              path, SDL_GetError() );
205     return -1;
206   }
207 
208   if(temp->format->palette == NULL) {
209     fprintf( stderr, "%s: Icon \"%s\" is not paletted\n", fuse_progname, path );
210     return -1;
211   }
212 
213   sdl_convert_icon( temp, red, 1 );
214   sdl_convert_icon( temp, green, 0 );
215 
216   SDL_FreeSurface( temp );
217 
218   return 0;
219 }
220 
221 int
uidisplay_init(int width,int height)222 uidisplay_init( int width, int height )
223 {
224   SDL_Rect **modes;
225   int no_modes;
226   int i = 0, mw = 0, mh = 0, mn = 0;
227 
228   /* Get available fullscreen/software modes */
229   modes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_SWSURFACE);
230 
231   no_modes = ( modes == (SDL_Rect **) 0 || modes == (SDL_Rect **) -1 ) ? 1 : 0;
232 
233   if( settings_current.sdl_fullscreen_mode &&
234       strcmp( settings_current.sdl_fullscreen_mode, "list" ) == 0 ) {
235 
236     fprintf( stderr,
237     "=====================================================================\n"
238     " List of available SDL fullscreen modes:\n"
239     "---------------------------------------------------------------------\n"
240     "  No. width height\n"
241     "---------------------------------------------------------------------\n"
242     );
243     if( no_modes ) {
244       fprintf( stderr, "  ** The modes list is empty%s...\n",
245                modes == (SDL_Rect **) -1 ? ", all resolutions allowed" : "" );
246     } else {
247       for( i = 0; modes[i]; i++ ) {
248         fprintf( stderr, "% 3d  % 5d % 5d\n", i + 1, modes[i]->w, modes[i]->h );
249       }
250     }
251     fprintf( stderr,
252     "=====================================================================\n");
253     fuse_exiting = 1;
254     return 0;
255   }
256 
257   if( !no_modes ) {
258     for( i=0; modes[i]; ++i ); /* count modes */
259   }
260 
261   if( settings_current.sdl_fullscreen_mode ) {
262     if( sscanf( settings_current.sdl_fullscreen_mode, " %dx%d", &mw, &mh ) != 2 ) {
263       if( !no_modes &&
264           sscanf( settings_current.sdl_fullscreen_mode, " %d", &mn ) == 1 &&
265           mn <= i ) {
266         mw = modes[mn - 1]->w; mh = modes[mn - 1]->h;
267       }
268     }
269   }
270 
271   /* Check if there are any modes available, or if our resolution is restricted
272      at all */
273   if( mh > 0 ) {
274     /* set from command line */
275     max_fullscreen_height = min_fullscreen_height = mh;
276     fullscreen_width = mw;
277   } else if( no_modes ){
278     /* Just try whatever we have and see what happens */
279     max_fullscreen_height = 480;
280     min_fullscreen_height = 240;
281   } else {
282     /* Record the largest supported fullscreen software mode */
283     max_fullscreen_height = modes[0]->h;
284 
285     /* Record the smallest supported fullscreen software mode */
286     for( i=0; modes[i]; ++i ) {
287       min_fullscreen_height = modes[i]->h;
288     }
289   }
290 
291   image_width = width;
292   image_height = height;
293 
294   timex = machine_current->timex;
295 
296   init_scalers();
297 
298   if ( scaler_select_scaler( current_scaler ) )
299     scaler_select_scaler( SCALER_NORMAL );
300 
301   if( sdldisplay_load_gfx_mode() ) return 1;
302 
303   SDL_WM_SetCaption( "Fuse", "Fuse" );
304 
305   /* We can now output error messages to our output device */
306   display_ui_initialised = 1;
307 
308   sdl_load_status_icon( "cassette.bmp", red_cassette, green_cassette );
309   sdl_load_status_icon( "microdrive.bmp", red_mdr, green_mdr );
310   sdl_load_status_icon( "plus3disk.bmp", red_disk, green_disk );
311 
312   return 0;
313 }
314 
315 static int
sdldisplay_allocate_colours(int numColours,Uint32 * colour_values,Uint32 * bw_values)316 sdldisplay_allocate_colours( int numColours, Uint32 *colour_values,
317                              Uint32 *bw_values )
318 {
319   int i;
320   Uint8 red, green, blue, grey;
321 
322   for( i = 0; i < numColours; i++ ) {
323 
324       red = colour_palette[i].r;
325     green = colour_palette[i].g;
326      blue = colour_palette[i].b;
327 
328     /* Addition of 0.5 is to avoid rounding errors */
329     grey = ( 0.299 * red + 0.587 * green + 0.114 * blue ) + 0.5;
330 
331     colour_values[i] = SDL_MapRGB( tmp_screen->format,  red, green, blue );
332     bw_values[i]     = SDL_MapRGB( tmp_screen->format, grey,  grey, grey );
333   }
334 
335   return 0;
336 }
337 
338 static void
sdldisplay_find_best_fullscreen_scaler(void)339 sdldisplay_find_best_fullscreen_scaler( void )
340 {
341   static int windowed_scaler = -1;
342   static int searching_fullscreen_scaler = 0;
343 
344   /* Make sure we have at least more than half of the screen covered in
345      fullscreen to avoid the "postage stamp" on machines that don't support
346      320x240 anymore e.g. Mac notebooks */
347   if( settings_current.full_screen ) {
348     int i = 0;
349 
350     if( searching_fullscreen_scaler ) return;
351     searching_fullscreen_scaler = 1;
352     while( i < SCALER_NUM &&
353            ( image_height*sdldisplay_current_size <= min_fullscreen_height/2 ||
354              image_height*sdldisplay_current_size > max_fullscreen_height ) ) {
355       if( windowed_scaler == -1) windowed_scaler = current_scaler;
356       while( !scaler_is_supported(i) ) i++;
357       scaler_select_scaler( i++ );
358       sdldisplay_current_size = scaler_get_scaling_factor( current_scaler );
359       /* if we failed to find a suitable size scaler, just use normal (what the
360          user had originally may be too big) */
361       if( image_height * sdldisplay_current_size <= min_fullscreen_height/2 ||
362           image_height * sdldisplay_current_size > max_fullscreen_height ) {
363         scaler_select_scaler( SCALER_NORMAL );
364         sdldisplay_current_size = scaler_get_scaling_factor( current_scaler );
365       }
366     }
367     searching_fullscreen_scaler = 0;
368   } else {
369     if( windowed_scaler != -1 ) {
370       scaler_select_scaler( windowed_scaler );
371       windowed_scaler = -1;
372       sdldisplay_current_size = scaler_get_scaling_factor( current_scaler );
373     }
374   }
375 }
376 
377 static int
sdldisplay_load_gfx_mode(void)378 sdldisplay_load_gfx_mode( void )
379 {
380   Uint16 *tmp_screen_pixels;
381 
382   sdldisplay_force_full_refresh = 1;
383 
384   /* Free the old surface */
385   if( tmp_screen ) {
386     free( tmp_screen->pixels );
387     SDL_FreeSurface( tmp_screen );
388     tmp_screen = NULL;
389   }
390 
391   tmp_screen_width = (image_width + 3);
392 
393   sdldisplay_current_size = scaler_get_scaling_factor( current_scaler );
394 
395   sdldisplay_find_best_fullscreen_scaler();
396 
397   /* Create the surface that contains the scaled graphics in 16 bit mode */
398   sdldisplay_gc = SDL_SetVideoMode(
399     settings_current.full_screen && fullscreen_width ? fullscreen_width :
400       image_width * sdldisplay_current_size,
401     settings_current.full_screen && fullscreen_width ? max_fullscreen_height :
402       image_height * sdldisplay_current_size,
403     16,
404     settings_current.full_screen ? (SDL_FULLSCREEN|SDL_SWSURFACE)
405                                  : SDL_SWSURFACE
406   );
407   if( !sdldisplay_gc ) {
408     fprintf( stderr, "%s: couldn't create SDL graphics context\n", fuse_progname );
409     fuse_abort();
410   }
411 
412   settings_current.full_screen =
413       !!( sdldisplay_gc->flags & ( SDL_FULLSCREEN | SDL_NOFRAME ) );
414   sdldisplay_is_full_screen = settings_current.full_screen;
415 
416   /* Distinguish 555 and 565 mode */
417   if( sdldisplay_gc->format->Gmask >> sdldisplay_gc->format->Gshift == 0x1f )
418     scaler_select_bitformat( 555 );
419   else
420     scaler_select_bitformat( 565 );
421 
422   /* Create the surface used for the graphics in 16 bit before scaling */
423 
424   /* Need some extra bytes around when using 2xSaI */
425   tmp_screen_pixels = (Uint16*)calloc(tmp_screen_width*(image_height+3), sizeof(Uint16));
426   tmp_screen = SDL_CreateRGBSurfaceFrom(tmp_screen_pixels,
427                                         tmp_screen_width,
428                                         image_height + 3,
429                                         16, tmp_screen_width*2,
430                                         sdldisplay_gc->format->Rmask,
431                                         sdldisplay_gc->format->Gmask,
432                                         sdldisplay_gc->format->Bmask,
433                                         sdldisplay_gc->format->Amask );
434 
435   if( !tmp_screen ) {
436     fprintf( stderr, "%s: couldn't create tmp_screen\n", fuse_progname );
437     fuse_abort();
438   }
439 
440   fullscreen_x_off = ( sdldisplay_gc->w - image_width * sdldisplay_current_size ) *
441                      sdldisplay_is_full_screen  / 2;
442   fullscreen_y_off = ( sdldisplay_gc->h - image_height * sdldisplay_current_size ) *
443                      sdldisplay_is_full_screen / 2;
444 
445   sdldisplay_allocate_colours( 16, colour_values, bw_values );
446 
447   /* Redraw the entire screen... */
448   display_refresh_all();
449 
450   return 0;
451 }
452 
453 int
uidisplay_hotswap_gfx_mode(void)454 uidisplay_hotswap_gfx_mode( void )
455 {
456   fuse_emulation_pause();
457 
458   /* Free the old surface */
459   if( tmp_screen ) {
460     free( tmp_screen->pixels );
461     SDL_FreeSurface( tmp_screen ); tmp_screen = NULL;
462   }
463 
464   /* Setup the new GFX mode */
465   if( sdldisplay_load_gfx_mode() ) return 1;
466 
467   /* reset palette */
468   SDL_SetColors( sdldisplay_gc, colour_palette, 0, 16 );
469 
470   /* Mac OS X resets the state of the cursor after a switch to full screen
471      mode */
472   if ( settings_current.full_screen || ui_mouse_grabbed ) {
473     SDL_ShowCursor( SDL_DISABLE );
474     SDL_WarpMouse( 128, 128 );
475   } else {
476     SDL_ShowCursor( SDL_ENABLE );
477   }
478 
479   fuse_emulation_unpause();
480 
481   return 0;
482 }
483 
484 SDL_Surface *saved = NULL;
485 
486 void
uidisplay_frame_save(void)487 uidisplay_frame_save( void )
488 {
489   if( saved ) {
490     SDL_FreeSurface( saved );
491     saved = NULL;
492   }
493 
494   saved = SDL_ConvertSurface( tmp_screen, tmp_screen->format,
495                               SDL_SWSURFACE );
496 }
497 
498 void
uidisplay_frame_restore(void)499 uidisplay_frame_restore( void )
500 {
501   if( saved ) {
502     SDL_BlitSurface( saved, NULL, tmp_screen, NULL );
503     sdldisplay_force_full_refresh = 1;
504   }
505 }
506 
507 static void
sdl_blit_icon(SDL_Surface ** icon,SDL_Rect * r,Uint32 tmp_screen_pitch,Uint32 dstPitch)508 sdl_blit_icon( SDL_Surface **icon,
509                SDL_Rect *r, Uint32 tmp_screen_pitch,
510                Uint32 dstPitch )
511 {
512   int x, y, w, h, dst_x, dst_y, dst_h;
513 
514   if( timex ) {
515     r->x<<=1;
516     r->y<<=1;
517     r->w<<=1;
518     r->h<<=1;
519   }
520 
521   x = r->x;
522   y = r->y;
523   w = r->w;
524   h = r->h;
525   r->x++;
526   r->y++;
527 
528   if( SDL_BlitSurface( icon[timex], NULL, tmp_screen, r ) ) return;
529 
530   /* Extend the dirty region by 1 pixel for scalers
531      that "smear" the screen, e.g. 2xSAI */
532   if( scaler_flags & SCALER_FLAGS_EXPAND )
533     scaler_expander( &x, &y, &w, &h, image_width, image_height );
534 
535   dst_y = y * sdldisplay_current_size + fullscreen_y_off;
536   dst_h = h;
537   dst_x = x * sdldisplay_current_size + fullscreen_x_off;
538 
539   scaler_proc16(
540 	(libspectrum_byte*)tmp_screen->pixels +
541 			(x+1) * tmp_screen->format->BytesPerPixel +
542 	                (y+1) * tmp_screen_pitch,
543 	tmp_screen_pitch,
544 	(libspectrum_byte*)sdldisplay_gc->pixels +
545 			dst_x * sdldisplay_gc->format->BytesPerPixel +
546 			dst_y * dstPitch,
547 	dstPitch, w, dst_h
548   );
549 
550   if( num_rects == MAX_UPDATE_RECT ) {
551     sdldisplay_force_full_refresh = 1;
552     return;
553   }
554 
555   /* Adjust rects for the destination rect size */
556   updated_rects[num_rects].x = dst_x;
557   updated_rects[num_rects].y = dst_y;
558   updated_rects[num_rects].w = w * sdldisplay_current_size;
559   updated_rects[num_rects].h = dst_h * sdldisplay_current_size;
560 
561   num_rects++;
562 }
563 
564 static void
sdl_icon_overlay(Uint32 tmp_screen_pitch,Uint32 dstPitch)565 sdl_icon_overlay( Uint32 tmp_screen_pitch, Uint32 dstPitch )
566 {
567   SDL_Rect r = { 243, 218, red_disk[0]->w, red_disk[0]->h };
568 
569   switch( sdl_disk_state ) {
570   case UI_STATUSBAR_STATE_ACTIVE:
571     sdl_blit_icon( green_disk, &r, tmp_screen_pitch, dstPitch );
572     break;
573   case UI_STATUSBAR_STATE_INACTIVE:
574     sdl_blit_icon( red_disk, &r, tmp_screen_pitch, dstPitch );
575     break;
576   case UI_STATUSBAR_STATE_NOT_AVAILABLE:
577     break;
578   }
579 
580   r.x = 264;
581   r.y = 218;
582   r.w = red_mdr[0]->w;
583   r.h = red_mdr[0]->h;
584 
585   switch( sdl_mdr_state ) {
586   case UI_STATUSBAR_STATE_ACTIVE:
587     sdl_blit_icon( green_mdr, &r, tmp_screen_pitch, dstPitch );
588     break;
589   case UI_STATUSBAR_STATE_INACTIVE:
590     sdl_blit_icon( red_mdr, &r, tmp_screen_pitch, dstPitch );
591     break;
592   case UI_STATUSBAR_STATE_NOT_AVAILABLE:
593     break;
594   }
595 
596   r.x = 285;
597   r.y = 220;
598   r.w = red_cassette[0]->w;
599   r.h = red_cassette[0]->h;
600 
601   switch( sdl_tape_state ) {
602   case UI_STATUSBAR_STATE_ACTIVE:
603     sdl_blit_icon( green_cassette, &r, tmp_screen_pitch, dstPitch );
604     break;
605   case UI_STATUSBAR_STATE_INACTIVE:
606   case UI_STATUSBAR_STATE_NOT_AVAILABLE:
607     sdl_blit_icon( red_cassette, &r, tmp_screen_pitch, dstPitch );
608     break;
609   }
610 
611   sdl_status_updated = 0;
612 }
613 
614 /* Set one pixel in the display */
615 void
uidisplay_putpixel(int x,int y,int colour)616 uidisplay_putpixel( int x, int y, int colour )
617 {
618   libspectrum_word *dest_base, *dest;
619   Uint32 *palette_values = settings_current.bw_tv ? bw_values :
620                            colour_values;
621 
622   Uint32 palette_colour = palette_values[ colour ];
623 
624   if( machine_current->timex ) {
625     x <<= 1; y <<= 1;
626     dest_base = dest =
627       (libspectrum_word*)( (libspectrum_byte*)tmp_screen->pixels +
628                            (x+1) * tmp_screen->format->BytesPerPixel +
629                            (y+1) * tmp_screen->pitch);
630 
631     *(dest++) = palette_colour;
632     *(dest++) = palette_colour;
633     dest = (libspectrum_word*)
634       ( (libspectrum_byte*)dest_base + tmp_screen->pitch);
635     *(dest++) = palette_colour;
636     *(dest++) = palette_colour;
637   } else {
638     dest =
639       (libspectrum_word*)( (libspectrum_byte*)tmp_screen->pixels +
640                            (x+1) * tmp_screen->format->BytesPerPixel +
641                            (y+1) * tmp_screen->pitch);
642 
643     *dest = palette_colour;
644   }
645 }
646 
647 /* Print the 8 pixels in `data' using ink colour `ink' and paper
648    colour `paper' to the screen at ( (8*x) , y ) */
649 void
uidisplay_plot8(int x,int y,libspectrum_byte data,libspectrum_byte ink,libspectrum_byte paper)650 uidisplay_plot8( int x, int y, libspectrum_byte data,
651 	         libspectrum_byte ink, libspectrum_byte paper )
652 {
653   libspectrum_word *dest;
654   Uint32 *palette_values = settings_current.bw_tv ? bw_values :
655                            colour_values;
656 
657   Uint32 palette_ink = palette_values[ ink ];
658   Uint32 palette_paper = palette_values[ paper ];
659 
660   if( machine_current->timex ) {
661     int i;
662     libspectrum_word *dest_base;
663 
664     x <<= 4; y <<= 1;
665 
666     dest_base =
667       (libspectrum_word*)( (libspectrum_byte*)tmp_screen->pixels +
668                            (x+1) * tmp_screen->format->BytesPerPixel +
669                            (y+1) * tmp_screen->pitch);
670 
671     for( i=0; i<2; i++ ) {
672       dest = dest_base;
673 
674       *(dest++) = ( data & 0x80 ) ? palette_ink : palette_paper;
675       *(dest++) = ( data & 0x80 ) ? palette_ink : palette_paper;
676       *(dest++) = ( data & 0x40 ) ? palette_ink : palette_paper;
677       *(dest++) = ( data & 0x40 ) ? palette_ink : palette_paper;
678       *(dest++) = ( data & 0x20 ) ? palette_ink : palette_paper;
679       *(dest++) = ( data & 0x20 ) ? palette_ink : palette_paper;
680       *(dest++) = ( data & 0x10 ) ? palette_ink : palette_paper;
681       *(dest++) = ( data & 0x10 ) ? palette_ink : palette_paper;
682       *(dest++) = ( data & 0x08 ) ? palette_ink : palette_paper;
683       *(dest++) = ( data & 0x08 ) ? palette_ink : palette_paper;
684       *(dest++) = ( data & 0x04 ) ? palette_ink : palette_paper;
685       *(dest++) = ( data & 0x04 ) ? palette_ink : palette_paper;
686       *(dest++) = ( data & 0x02 ) ? palette_ink : palette_paper;
687       *(dest++) = ( data & 0x02 ) ? palette_ink : palette_paper;
688       *(dest++) = ( data & 0x01 ) ? palette_ink : palette_paper;
689       *dest     = ( data & 0x01 ) ? palette_ink : palette_paper;
690 
691       dest_base = (libspectrum_word*)
692         ( (libspectrum_byte*)dest_base + tmp_screen->pitch);
693     }
694   } else {
695     x <<= 3;
696 
697     dest =
698       (libspectrum_word*)( (libspectrum_byte*)tmp_screen->pixels +
699                            (x+1) * tmp_screen->format->BytesPerPixel +
700                            (y+1) * tmp_screen->pitch);
701 
702     *(dest++) = ( data & 0x80 ) ? palette_ink : palette_paper;
703     *(dest++) = ( data & 0x40 ) ? palette_ink : palette_paper;
704     *(dest++) = ( data & 0x20 ) ? palette_ink : palette_paper;
705     *(dest++) = ( data & 0x10 ) ? palette_ink : palette_paper;
706     *(dest++) = ( data & 0x08 ) ? palette_ink : palette_paper;
707     *(dest++) = ( data & 0x04 ) ? palette_ink : palette_paper;
708     *(dest++) = ( data & 0x02 ) ? palette_ink : palette_paper;
709     *dest     = ( data & 0x01 ) ? palette_ink : palette_paper;
710   }
711 }
712 
713 /* Print the 16 pixels in `data' using ink colour `ink' and paper
714    colour `paper' to the screen at ( (16*x) , y ) */
715 void
uidisplay_plot16(int x,int y,libspectrum_word data,libspectrum_byte ink,libspectrum_byte paper)716 uidisplay_plot16( int x, int y, libspectrum_word data,
717 		  libspectrum_byte ink, libspectrum_byte paper )
718 {
719   libspectrum_word *dest_base, *dest;
720   int i;
721   Uint32 *palette_values = settings_current.bw_tv ? bw_values :
722                            colour_values;
723   Uint32 palette_ink = palette_values[ ink ];
724   Uint32 palette_paper = palette_values[ paper ];
725   x <<= 4; y <<= 1;
726 
727   dest_base =
728     (libspectrum_word*)( (libspectrum_byte*)tmp_screen->pixels +
729                          (x+1) * tmp_screen->format->BytesPerPixel +
730                          (y+1) * tmp_screen->pitch);
731 
732   for( i=0; i<2; i++ ) {
733     dest = dest_base;
734 
735     *(dest++) = ( data & 0x8000 ) ? palette_ink : palette_paper;
736     *(dest++) = ( data & 0x4000 ) ? palette_ink : palette_paper;
737     *(dest++) = ( data & 0x2000 ) ? palette_ink : palette_paper;
738     *(dest++) = ( data & 0x1000 ) ? palette_ink : palette_paper;
739     *(dest++) = ( data & 0x0800 ) ? palette_ink : palette_paper;
740     *(dest++) = ( data & 0x0400 ) ? palette_ink : palette_paper;
741     *(dest++) = ( data & 0x0200 ) ? palette_ink : palette_paper;
742     *(dest++) = ( data & 0x0100 ) ? palette_ink : palette_paper;
743     *(dest++) = ( data & 0x0080 ) ? palette_ink : palette_paper;
744     *(dest++) = ( data & 0x0040 ) ? palette_ink : palette_paper;
745     *(dest++) = ( data & 0x0020 ) ? palette_ink : palette_paper;
746     *(dest++) = ( data & 0x0010 ) ? palette_ink : palette_paper;
747     *(dest++) = ( data & 0x0008 ) ? palette_ink : palette_paper;
748     *(dest++) = ( data & 0x0004 ) ? palette_ink : palette_paper;
749     *(dest++) = ( data & 0x0002 ) ? palette_ink : palette_paper;
750     *dest     = ( data & 0x0001 ) ? palette_ink : palette_paper;
751 
752     dest_base = (libspectrum_word*)
753       ( (libspectrum_byte*)dest_base + tmp_screen->pitch);
754   }
755 }
756 
757 void
uidisplay_frame_end(void)758 uidisplay_frame_end( void )
759 {
760   SDL_Rect *r;
761   Uint32 tmp_screen_pitch, dstPitch;
762   SDL_Rect *last_rect;
763 
764   /* We check for a switch to fullscreen here to give systems with a
765      windowed-only UI a chance to free menu etc. resources before
766      the switch to fullscreen (e.g. Mac OS X) */
767   if( sdldisplay_is_full_screen != settings_current.full_screen &&
768       uidisplay_hotswap_gfx_mode() ) {
769     fprintf( stderr, "%s: Error switching to fullscreen\n", fuse_progname );
770     fuse_abort();
771   }
772 
773   /* Force a full redraw if requested */
774   if ( sdldisplay_force_full_refresh ) {
775     num_rects = 1;
776 
777     updated_rects[0].x = 0;
778     updated_rects[0].y = 0;
779     updated_rects[0].w = image_width;
780     updated_rects[0].h = image_height;
781   }
782 
783   if ( !(ui_widget_level >= 0) && num_rects == 0 && !sdl_status_updated )
784     return;
785 
786   if( SDL_MUSTLOCK( sdldisplay_gc ) ) SDL_LockSurface( sdldisplay_gc );
787 
788   tmp_screen_pitch = tmp_screen->pitch;
789 
790   dstPitch = sdldisplay_gc->pitch;
791 
792   last_rect = updated_rects + num_rects;
793 
794   for( r = updated_rects; r != last_rect; r++ ) {
795 
796     int dst_y = r->y * sdldisplay_current_size + fullscreen_y_off;
797     int dst_h = r->h;
798     int dst_x = r->x * sdldisplay_current_size + fullscreen_x_off;
799 
800     scaler_proc16(
801       (libspectrum_byte*)tmp_screen->pixels +
802                         (r->x+1) * tmp_screen->format->BytesPerPixel +
803 	                (r->y+1)*tmp_screen_pitch,
804       tmp_screen_pitch,
805       (libspectrum_byte*)sdldisplay_gc->pixels +
806 	                 dst_x * sdldisplay_gc->format->BytesPerPixel +
807 			 dst_y*dstPitch,
808       dstPitch, r->w, dst_h
809     );
810 
811     /* Adjust rects for the destination rect size */
812     r->x = dst_x;
813     r->y = dst_y;
814     r->w *= sdldisplay_current_size;
815     r->h = dst_h * sdldisplay_current_size;
816   }
817 
818   if ( settings_current.statusbar )
819     sdl_icon_overlay( tmp_screen_pitch, dstPitch );
820 
821   if( SDL_MUSTLOCK( sdldisplay_gc ) ) SDL_UnlockSurface( sdldisplay_gc );
822 
823   /* Finally, blit all our changes to the screen */
824   SDL_UpdateRects( sdldisplay_gc, num_rects, updated_rects );
825 
826   num_rects = 0;
827   sdldisplay_force_full_refresh = 0;
828 }
829 
830 void
uidisplay_area(int x,int y,int width,int height)831 uidisplay_area( int x, int y, int width, int height )
832 {
833   if ( sdldisplay_force_full_refresh )
834     return;
835 
836   if( num_rects == MAX_UPDATE_RECT ) {
837     sdldisplay_force_full_refresh = 1;
838     return;
839   }
840 
841   /* Extend the dirty region by 1 pixel for scalers
842      that "smear" the screen, e.g. 2xSAI */
843   if( scaler_flags & SCALER_FLAGS_EXPAND )
844     scaler_expander( &x, &y, &width, &height, image_width, image_height );
845 
846   updated_rects[num_rects].x = x;
847   updated_rects[num_rects].y = y;
848   updated_rects[num_rects].w = width;
849   updated_rects[num_rects].h = height;
850 
851   num_rects++;
852 }
853 
854 int
uidisplay_end(void)855 uidisplay_end( void )
856 {
857   int i;
858 
859   display_ui_initialised = 0;
860 
861   if ( tmp_screen ) {
862     free( tmp_screen->pixels );
863     SDL_FreeSurface( tmp_screen ); tmp_screen = NULL;
864   }
865 
866   if( saved ) {
867     SDL_FreeSurface( saved ); saved = NULL;
868   }
869 
870   for( i=0; i<2; i++ ) {
871     if ( red_cassette[i] ) {
872       SDL_FreeSurface( red_cassette[i] ); red_cassette[i] = NULL;
873     }
874     if ( green_cassette[i] ) {
875       SDL_FreeSurface( green_cassette[i] ); green_cassette[i] = NULL;
876     }
877     if ( red_mdr[i] ) {
878       SDL_FreeSurface( red_mdr[i] ); red_mdr[i] = NULL;
879     }
880     if ( green_mdr[i] ) {
881       SDL_FreeSurface( green_mdr[i] ); green_mdr[i] = NULL;
882     }
883     if ( red_disk[i] ) {
884       SDL_FreeSurface( red_disk[i] ); red_disk[i] = NULL;
885     }
886     if ( green_disk[i] ) {
887       SDL_FreeSurface( green_disk[i] ); green_disk[i] = NULL;
888     }
889   }
890 
891   return 0;
892 }
893 
894 /* The statusbar handling function */
895 int
ui_statusbar_update(ui_statusbar_item item,ui_statusbar_state state)896 ui_statusbar_update( ui_statusbar_item item, ui_statusbar_state state )
897 {
898   switch( item ) {
899 
900   case UI_STATUSBAR_ITEM_DISK:
901     sdl_disk_state = state;
902     sdl_status_updated = 1;
903     return 0;
904 
905   case UI_STATUSBAR_ITEM_PAUSED:
906     /* We don't support pausing this version of Fuse */
907     return 0;
908 
909   case UI_STATUSBAR_ITEM_TAPE:
910     sdl_tape_state = state;
911     sdl_status_updated = 1;
912     return 0;
913 
914   case UI_STATUSBAR_ITEM_MICRODRIVE:
915     sdl_mdr_state = state;
916     sdl_status_updated = 1;
917     return 0;
918 
919   case UI_STATUSBAR_ITEM_MOUSE:
920     /* We don't support showing a grab icon */
921     return 0;
922 
923   }
924 
925   ui_error( UI_ERROR_ERROR, "Attempt to update unknown statusbar item %d",
926             item );
927   return 1;
928 }
929