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