Ticket #7963: sdl.cpp

File sdl.cpp, 28.6 KB (added by SF/luke_br, 18 years ago)

OpenGL render in SDL (Ops, this is the Final)

Line 
1/* ScummVM - Scumm Interpreter
2 * Copyright (C) 2001 Ludvig Strigeus
3 * Copyright (C) 2001/2002 The ScummVM project
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * $Header: /cvsroot/scummvm/scummvm/sdl.cpp,v 1.112 2002/05/04 01:16:06 fingolfin Exp $
20 *
21 */
22
23#include "stdafx.h"
24#include "scumm.h"
25#include "mididrv.h"
26#include "SDL_thread.h"
27#include "gameDetector.h"
28
29#include "scummvm.xpm"
30
31#include <SDL.h>
32
33#ifdef OPENGL
34#include "fb2opengl.h"
35#define GL_SIZE 320*200
36char gl_black[GL_SIZE];
37int gl_shake;
38
39void gl_init() {
40 bzero(gl_black,GL_SIZE);
41 gl_shake=0;
42}
43
44void gl_update(void *buf) {
45 fb2gl_update(buf,320,200,320,0,gl_shake);
46}
47#endif
48
49#define MAX(a,b) (((a)<(b)) ? (b) : (a))
50#define MIN(a,b) (((a)>(b)) ? (b) : (a))
51
52class OSystem_SDL : public OSystem {
53public:
54 // Set colors of the palette
55 void set_palette(const byte *colors, uint start, uint num);
56
57 // Set the size of the video bitmap.
58 // Typically, 320x200
59 void init_size(uint w, uint h);
60
61 // Draw a bitmap to screen.
62 // The screen will not be updated to reflect the new bitmap
63 void copy_rect(const byte *buf, int pitch, int x, int y, int w, int h);
64
65 // Update the dirty areas of the screen
66 void update_screen();
67
68 // Either show or hide the mouse cursor
69 bool show_mouse(bool visible);
70
71 // Set the position of the mouse cursor
72 void set_mouse_pos(int x, int y);
73
74 // Set the bitmap that's used when drawing the cursor.
75 void set_mouse_cursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y);
76
77 // Shaking is used in SCUMM. Set current shake position.
78 void set_shake_pos(int shake_pos);
79
80 // Get the number of milliseconds since the program was started.
81 uint32 get_msecs();
82
83 // Delay for a specified amount of milliseconds
84 void delay_msecs(uint msecs);
85
86 // Create a thread
87 void *create_thread(ThreadProc *proc, void *param);
88
89 // Get the next event.
90 // Returns true if an event was retrieved.
91 bool poll_event(Event *event);
92
93 // Set function that generates samples
94 bool set_sound_proc(void *param, SoundProc *proc, byte sound);
95
96 // Poll cdrom status
97 // Returns true if cd audio is playing
98 bool poll_cdrom();
99
100 // Play cdrom audio track
101 void play_cdrom(int track, int num_loops, int start_frame, int end_frame);
102
103 // Stop cdrom audio track
104 void stop_cdrom();
105
106 // Update cdrom audio status
107 void update_cdrom();
108
109 // Quit
110 void quit();
111
112 // Set a parameter
113 uint32 property(int param, uint32 value);
114
115 static OSystem *create(int gfx_mode, bool full_screen);
116
117private:
118 typedef void TwoXSaiProc(uint8 *srcPtr, uint32 srcPitch, uint8 *deltaPtr,
119 uint8 *dstPtr, uint32 dstPitch, int width, int height);
120
121 SDL_Surface *sdl_screen;
122 SDL_Surface *sdl_hwscreen;
123 SDL_Surface *sdl_tmpscreen;
124 SDL_Surface *sdl_palscreen;
125 SDL_CD *cdrom;
126
127 enum {
128 DF_FORCE_FULL_ON_PALETTE = 1,
129 DF_WANT_RECT_OPTIM = 2,
130 DF_2xSAI = 4,
131 DF_SEPARATE_TEMPSCREEN = 8,
132 DF_UPDATE_EXPAND_1_PIXEL = 16,
133 };
134
135 int _mode;
136 bool _full_screen;
137 bool _mouse_visible;
138 bool _mouse_drawn;
139 uint32 _mode_flags;
140
141 bool force_full; //Force full redraw on next update_screen
142 bool cksum_valid;
143
144 enum {
145 NUM_DIRTY_RECT = 100,
146 SCREEN_WIDTH = 320,
147 SCREEN_HEIGHT = 200,
148 CKSUM_NUM = (SCREEN_WIDTH*SCREEN_HEIGHT/(8*8)),
149
150 MAX_MOUSE_W = 40,
151 MAX_MOUSE_H = 40,
152 MAX_SCALING = 3,
153 };
154
155 SDL_Rect *dirty_rect_list;
156 int num_dirty_rects;
157 uint32 *dirty_checksums;
158
159 int scaling;
160
161 /* CD Audio */
162 int cd_track, cd_num_loops, cd_start_frame, cd_end_frame;
163 Uint32 cd_end_time, cd_stop_time, cd_next_second;
164
165 struct MousePos {
166 int16 x,y,w,h;
167 };
168
169 byte *_ms_buf;
170 byte *_ms_backup;
171 MousePos _ms_cur;
172 MousePos _ms_old;
173 int16 _ms_hotspot_x;
174 int16 _ms_hotspot_y;
175 int _current_shake_pos;
176 int _new_shake_pos;
177 TwoXSaiProc *_sai_func;
178 SDL_Color *_cur_pal;
179
180 uint _palette_changed_first, _palette_changed_last;
181
182 OSystem_SDL() : _current_shake_pos(0), _new_shake_pos(0) {}
183
184 void add_dirty_rgn_auto(const byte *buf);
185 void mk_checksums(const byte *buf);
186
187 static void fill_sound(void *userdata, Uint8 * stream, int len);
188
189 void add_dirty_rect(int x, int y, int w, int h);
190
191 void draw_mouse();
192 void undraw_mouse();
193
194 void load_gfx_mode();
195 void unload_gfx_mode();
196
197 void hotswap_gfx_mode();
198
199 void get_320x200_image(byte *buf);
200 static uint32 autosave(uint32);
201
202 void setup_icon();
203};
204
205int Init_2xSaI (uint32 BitFormat);
206void _2xSaI(uint8 *srcPtr, uint32 srcPitch, uint8 *deltaPtr, uint8 *dstPtr,
207 uint32 dstPitch, int width, int height);
208void Super2xSaI(uint8 *srcPtr, uint32 srcPitch, uint8 *deltaPtr,
209 uint8 *dstPtr, uint32 dstPitch, int width, int height);
210void SuperEagle(uint8 *srcPtr, uint32 srcPitch, uint8 *deltaPtr,
211 uint8 *dstPtr, uint32 dstPitch, int width, int height);
212void AdvMame2x(uint8 *srcPtr, uint32 srcPitch, uint8 *null,
213 uint8 *dstPtr, uint32 dstPitch, int width, int height);
214void Normal1x(uint8 *srcPtr, uint32 srcPitch, uint8 *null,
215 uint8 *dstPtr, uint32 dstPitch, int width, int height);
216void Normal2x(uint8 *srcPtr, uint32 srcPitch, uint8 *null,
217 uint8 *dstPtr, uint32 dstPitch, int width, int height);
218void Normal3x(uint8 *srcPtr, uint32 srcPitch, uint8 *null,
219 uint8 *dstPtr, uint32 dstPitch, int width, int height);
220
221void atexit_proc() {
222 SDL_ShowCursor(SDL_ENABLE);
223 SDL_Quit();
224}
225
226uint32 OSystem_SDL::autosave(uint32 interval)
227{
228 g_scumm->_doAutosave = true;
229
230 return interval;
231}
232
233
234OSystem *OSystem_SDL::create(int gfx_mode, bool full_screen) {
235 OSystem_SDL *syst = new OSystem_SDL();
236 syst->_mode = gfx_mode;
237 syst->_full_screen = full_screen;
238
239 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) ==-1) {
240 error("Could not initialize SDL: %s.\n", SDL_GetError());
241 }
242
243 SDL_ShowCursor(SDL_DISABLE);
244 SDL_SetTimer(5 * 60 * 1000, (SDL_TimerCallback) autosave);
245
246 /* Setup the icon */
247 syst->setup_icon();
248
249 /* Clean up on exit */
250 atexit(atexit_proc);
251
252 return syst;
253}
254
255OSystem *OSystem_SDL_create(int gfx_mode, bool full_screen) {
256 return OSystem_SDL::create(gfx_mode, full_screen);
257}
258
259void OSystem_SDL::set_palette(const byte *colors, uint start, uint num) {
260 const byte *b = colors;
261 uint i;
262 SDL_Color *base = _cur_pal + start;
263 for(i=0;i!=num;i++) {
264 #ifdef OPENGL
265 fb2gl_palette(i+start,b[0],b[1],b[2]);
266 #else
267 base[i].r = b[0];
268 base[i].g = b[1];
269 base[i].b = b[2];
270 #endif
271 b += 4;
272 }
273
274 if (start < _palette_changed_first)
275 _palette_changed_first = start;
276
277 if (start + num > _palette_changed_last)
278 _palette_changed_last = start + num;
279}
280
281void OSystem_SDL::load_gfx_mode() {
282 force_full = true;
283 scaling = 1;
284 _mode_flags = 0;
285
286 _sai_func = NULL;
287 sdl_tmpscreen = NULL;
288
289#ifdef OPENGL
290 scaling=1;
291#else
292 switch(_mode) {
293 case GFX_2XSAI:
294 scaling = 2;
295 _sai_func = _2xSaI;
296 break;
297 case GFX_SUPER2XSAI:
298 scaling = 2;
299 _sai_func = Super2xSaI;
300 break;
301 case GFX_SUPEREAGLE:
302 scaling = 2;
303 _sai_func = SuperEagle;
304 break;
305 case GFX_ADVMAME2X:
306 scaling = 2;
307 _sai_func = AdvMame2x;
308 break;
309
310 case GFX_DOUBLESIZE:
311 scaling = 2;
312 break;
313
314 case GFX_TRIPLESIZE:
315 if (_full_screen) {
316 warning("full screen in useless in triplesize mode, reverting to normal mode");
317 goto normal_mode;
318 }
319 scaling = 3;
320 break;
321
322 case GFX_NORMAL:
323normal_mode:;
324 scaling = 1;
325 break;
326 }
327
328#endif
329
330 sdl_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 200, 8, 0, 0, 0, 0);
331 if (sdl_screen == NULL)
332 error("sdl_screen failed failed");
333
334 if (_sai_func) {
335 uint16 *tmp_screen = (uint16*)calloc((320+3)*(200+3),sizeof(uint16));
336 _mode_flags = DF_FORCE_FULL_ON_PALETTE | DF_WANT_RECT_OPTIM | DF_2xSAI | DF_SEPARATE_TEMPSCREEN | DF_UPDATE_EXPAND_1_PIXEL;
337
338 sdl_hwscreen = SDL_SetVideoMode(320 * scaling, 200 * scaling, 16,
339 _full_screen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
340 );
341 if (sdl_hwscreen == NULL)
342 error("sdl_hwscreen failed");
343
344 /* Need some extra bytes around when using 2XSAI */
345 if (sdl_hwscreen->format->Rmask == 0x7C00) // HACK HACK HACK
346 Init_2xSaI(555);
347 else
348 Init_2xSaI(565);
349 sdl_tmpscreen = SDL_CreateRGBSurfaceFrom(tmp_screen,
350 320 + 3, 200 + 3, 16, (320 + 3)*2,
351 sdl_hwscreen->format->Rmask,
352 sdl_hwscreen->format->Gmask,
353 sdl_hwscreen->format->Bmask,
354 sdl_hwscreen->format->Amask);
355
356 if (sdl_tmpscreen == NULL)
357 error("sdl_tmpscreen failed");
358
359 sdl_palscreen = sdl_screen;
360 } else {
361 switch(scaling) {
362 case 3:
363 _sai_func = Normal3x;
364 break;
365 case 2:
366 _sai_func = Normal2x;
367 break;
368 case 1:
369 _sai_func = Normal1x;
370 break;
371 }
372
373 _mode_flags = DF_WANT_RECT_OPTIM;
374
375 #ifdef OPENGL
376 fb2gl_init(640,480,0,70, FB2GL_FS | FB2GL_320 | FB2GL_PITCH);
377 gl_init();
378 #else
379 sdl_hwscreen = SDL_SetVideoMode(320 * scaling, 200 * scaling, 8,
380 _full_screen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
381 );
382
383 if (sdl_hwscreen == NULL)
384 error("sdl_hwscreen failed");
385 #endif
386
387 sdl_tmpscreen = sdl_screen;
388 sdl_palscreen = sdl_hwscreen;
389 }
390}
391
392void OSystem_SDL::unload_gfx_mode() {
393 SDL_FreeSurface(sdl_screen);
394 sdl_screen = NULL;
395
396#ifndef OPENGL
397 SDL_FreeSurface(sdl_hwscreen);
398#endif
399 sdl_hwscreen = NULL;
400
401 if (_mode_flags & DF_SEPARATE_TEMPSCREEN) {
402 free((uint16*)sdl_tmpscreen->pixels);
403 SDL_FreeSurface(sdl_tmpscreen);
404 }
405 sdl_tmpscreen = NULL;
406}
407
408void OSystem_SDL::init_size(uint w, uint h) {
409 if (w != SCREEN_WIDTH && h != SCREEN_HEIGHT)
410 error("320x200 is the only game resolution supported");
411
412 /* allocate palette, it needs to be persistent across
413 * driver changes, so i'll alloc it here */
414 _cur_pal = (SDL_Color*)calloc(sizeof(SDL_Color), 256);
415
416 dirty_rect_list = (SDL_Rect*)calloc(NUM_DIRTY_RECT, sizeof(SDL_Rect));
417 _ms_backup = (byte*)malloc(MAX_MOUSE_W * MAX_MOUSE_H * MAX_SCALING);
418 dirty_checksums = (uint32*)calloc(CKSUM_NUM*2, sizeof(uint32));
419
420 load_gfx_mode();
421}
422
423void OSystem_SDL::copy_rect(const byte *buf, int pitch, int x, int y, int w, int h) {
424 if (sdl_screen == NULL)
425 return;
426
427 if (pitch == SCREEN_WIDTH && x==0 && y==0 && w==SCREEN_WIDTH && h==SCREEN_HEIGHT && _mode_flags&DF_WANT_RECT_OPTIM) {
428 /* Special, optimized case for full screen updates.
429 * It tries to determine what areas were actually changed,
430 * and just updates those, on the actual display. */
431 add_dirty_rgn_auto(buf);
432 } else {
433 /* Clip the coordinates */
434 if (x < 0) { w+=x; buf-=x; x = 0; }
435 if (y < 0) { h+=y; buf-=y*pitch; y = 0; }
436 if (w > SCREEN_WIDTH-x) { w = SCREEN_WIDTH - x; }
437 if (h > SCREEN_HEIGHT-y) { h = SCREEN_HEIGHT - y; }
438
439 if (w <= 0 || h <= 0)
440 return;
441
442 cksum_valid = false;
443 add_dirty_rect(x, y, w, h);
444 }
445
446 /* FIXME: undraw mouse only if the draw rect intersects with the mouse rect */
447 if (_mouse_drawn)
448 undraw_mouse();
449
450
451 if (SDL_LockSurface(sdl_screen) == -1)
452 error("SDL_LockSurface failed: %s.\n", SDL_GetError());
453
454 byte *dst = (byte *)sdl_screen->pixels + y * 320 + x;
455 do {
456 memcpy(dst, buf, w);
457 dst += 320;
458 buf += pitch;
459 } while (--h);
460
461 SDL_UnlockSurface(sdl_screen);
462}
463
464
465void OSystem_SDL::add_dirty_rect(int x, int y, int w, int h) {
466 if (force_full)
467 return;
468
469 if (num_dirty_rects == NUM_DIRTY_RECT)
470 force_full = true;
471 else {
472 SDL_Rect *r = &dirty_rect_list[num_dirty_rects++];
473
474 /* Update the dirty region by 1 pixel for graphics drivers
475 * that "smear" the screen */
476 if (_mode_flags & DF_UPDATE_EXPAND_1_PIXEL) {
477 x--;
478 y--;
479 w+=2;
480 h+=2;
481 }
482
483 /* clip */
484 if (x < 0) { w+=x; x=0; }
485 if (y < 0) { h+=y; y=0; }
486 if (w > SCREEN_WIDTH-x) { w = SCREEN_WIDTH - x; }
487 if (h > SCREEN_HEIGHT-y) { h = SCREEN_HEIGHT - y; }
488
489 r->x = x;
490 r->y = y;
491 r->w = w;
492 r->h = h;
493 }
494}
495
496#define ROL(a,n) a = (a<<(n)) | (a>>(32-(n)))
497#define DOLINE(x) a ^= ((uint32*)buf)[0+(x)*(SCREEN_WIDTH/4)]; b ^= ((uint32*)buf)[1+(x)*(SCREEN_WIDTH/4)]
498void OSystem_SDL::mk_checksums(const byte *buf) {
499 uint32 *sums = dirty_checksums;
500 uint x,y;
501
502 /* the 8x8 blocks in buf are enumerated starting in the top left corner and
503 * reading each line at a time from left to right */
504 for(y=0; y!=SCREEN_HEIGHT/8; y++,buf+=SCREEN_WIDTH*(8-1))
505 for(x=0; x!=SCREEN_WIDTH/8; x++,buf+=8) {
506 uint32 a = x;
507 uint32 b = y;
508
509 DOLINE(0); ROL(a,13); ROL(b,11);
510 DOLINE(2); ROL(a,13); ROL(b,11);
511 DOLINE(4); ROL(a,13); ROL(b,11);
512 DOLINE(6); ROL(a,13); ROL(b,11);
513
514 a*=0xDEADBEEF;
515 b*=0xBAADF00D;
516
517 DOLINE(1); ROL(a,13); ROL(b,11);
518 DOLINE(3); ROL(a,13); ROL(b,11);
519 DOLINE(5); ROL(a,13); ROL(b,11);
520 DOLINE(7); ROL(a,13); ROL(b,11);
521
522 /* output the checksum for this block */
523 *sums++=a+b;
524 }
525}
526#undef DOLINE
527#undef ROL
528
529
530void OSystem_SDL::add_dirty_rgn_auto(const byte *buf) {
531 assert( ((uint32)buf & 3) == 0);
532
533 /* generate a table of the checksums */
534 mk_checksums(buf);
535
536 if (!cksum_valid) {
537 force_full = true;
538 cksum_valid = true;
539 }
540
541 /* go through the checksum list, compare it with the previous checksums,
542 and add all dirty rectangles to a list. try to combine small rectangles
543 into bigger ones in a simple way */
544 if (!force_full) {
545 uint x,y,w;
546 uint32 *ck = dirty_checksums;
547
548 for(y=0; y!=SCREEN_HEIGHT/8; y++) {
549 for(x=0; x!=SCREEN_WIDTH/8; x++,ck++) {
550 if (ck[0] != ck[CKSUM_NUM]) {
551 /* found a dirty 8x8 block, now go as far to the right as possible,
552 and at the same time, unmark the dirty status by setting old to new. */
553 w=0;
554 do {
555 ck[w+CKSUM_NUM] = ck[w];
556 w++;
557 } while (x+w != SCREEN_WIDTH/8 && ck[w] != ck[w+CKSUM_NUM]);
558
559 add_dirty_rect(x*8, y*8, w*8, 8);
560
561 if (force_full)
562 goto get_out;
563 }
564 }
565 }
566 } else {
567 get_out:;
568 /* Copy old checksums to new */
569 memcpy(dirty_checksums + CKSUM_NUM, dirty_checksums, CKSUM_NUM * sizeof(uint32));
570 }
571}
572
573void OSystem_SDL::update_screen() {
574 /* First make sure the mouse is drawn, if it should be drawn. */
575 draw_mouse();
576
577
578 if (_palette_changed_last != 0) {
579 #ifdef OPENGL
580 fb2gl_set_palette(_palette_changed_first,
581 _palette_changed_last - _palette_changed_first);
582 gl_update(sdl_tmpscreen->pixels);
583 #else
584 SDL_SetColors(sdl_palscreen, _cur_pal + _palette_changed_first,
585 _palette_changed_first,
586 _palette_changed_last - _palette_changed_first);
587 #endif
588
589 _palette_changed_last = 0;
590
591 if (_mode_flags & DF_FORCE_FULL_ON_PALETTE)
592 force_full = true;
593 }
594
595 if (_current_shake_pos != _new_shake_pos) {
596
597 /* Fill the dirty area with blackness or the scumm image */
598 SDL_Rect blackrect = {0, 0, SCREEN_WIDTH*scaling, _new_shake_pos*scaling};
599 #ifdef OPENGL
600 // gl_black == blackness
601 fb2gl_update(gl_black,SCREEN_WIDTH,_new_shake_pos,SCREEN_WIDTH,0,0);
602 gl_shake=_new_shake_pos;
603 #else
604 SDL_FillRect(sdl_hwscreen, &blackrect, 0);
605 #endif
606
607 _current_shake_pos = _new_shake_pos;
608
609 force_full = true;
610 }
611#ifndef OPENGL
612
613 /* force a full redraw if requested */
614 if (force_full) {
615 num_dirty_rects = 1;
616
617 dirty_rect_list[0].x = 0;
618 dirty_rect_list[0].y = 0;
619 dirty_rect_list[0].w = SCREEN_WIDTH;
620 dirty_rect_list[0].h = SCREEN_HEIGHT;
621 }
622
623 if (num_dirty_rects == 0 || sdl_hwscreen == NULL)
624 return;
625
626 SDL_Rect *r;
627 uint32 srcPitch, dstPitch;
628 SDL_Rect *last_rect = dirty_rect_list + num_dirty_rects;
629
630
631 /* Convert appropriate parts of the image into 16bpp */
632 if (_mode_flags & DF_2xSAI) {
633 SDL_Rect dst;
634 for(r=dirty_rect_list; r!=last_rect; ++r) {
635 dst = *r;
636 dst.x++;
637 dst.y++;
638 if (SDL_BlitSurface(sdl_screen, r, sdl_tmpscreen, &dst) != 0)
639 error("SDL_BlitSurface failed: %s", SDL_GetError());
640 }
641 }
642
643 SDL_LockSurface(sdl_tmpscreen);
644
645 SDL_LockSurface(sdl_hwscreen);
646
647 srcPitch = sdl_tmpscreen->pitch;
648 dstPitch = sdl_hwscreen->pitch;
649
650
651 if (_mode_flags & DF_2xSAI) {
652 for(r=dirty_rect_list; r!=last_rect; ++r) {
653 register int dst_y = r->y + _current_shake_pos;
654 register int dst_h = 0;
655 if (dst_y < SCREEN_HEIGHT) {
656 dst_h = r->h;
657 if (dst_h > SCREEN_HEIGHT - dst_y)
658 dst_h = SCREEN_HEIGHT - dst_y;
659
660 r->x <<= 1;
661 dst_y <<= 1;
662
663 _sai_func((byte*)sdl_tmpscreen->pixels + (r->x+2) + (r->y+1)*srcPitch, srcPitch, NULL,
664 (byte*)sdl_hwscreen->pixels + r->x*scaling + dst_y*dstPitch, dstPitch, r->w, dst_h);
665 }
666
667 r->y = dst_y;
668 r->w <<= 1;
669 r->h = dst_h << 1;
670 }
671 } else {
672 for(r=dirty_rect_list; r!=last_rect; ++r) {
673 register int dst_y = r->y + _current_shake_pos;
674 register int dst_h = 0;
675 if (dst_y < SCREEN_HEIGHT) {
676 dst_h = r->h;
677 if (dst_h > SCREEN_HEIGHT - dst_y)
678 dst_h = SCREEN_HEIGHT - dst_y;
679
680 dst_y *= scaling;
681
682 _sai_func((byte*)sdl_tmpscreen->pixels + r->x + r->y*srcPitch, srcPitch, NULL,
683 (byte*)sdl_hwscreen->pixels + r->x*scaling + dst_y*dstPitch, dstPitch, r->w, dst_h);
684 }
685
686 r->x *= scaling;
687 r->y = dst_y;
688 r->w *= scaling;
689 r->h = dst_h * scaling;
690 }
691 }
692
693 if (force_full) {
694 dirty_rect_list[0].y = 0;
695 dirty_rect_list[0].h = SCREEN_HEIGHT * scaling;
696 }
697
698 SDL_UnlockSurface(sdl_tmpscreen);
699 SDL_UnlockSurface(sdl_hwscreen);
700
701 SDL_UpdateRects(sdl_hwscreen, num_dirty_rects, dirty_rect_list);
702
703
704 num_dirty_rects = 0;
705 force_full = false;
706
707#else /* OpenGL */
708 else gl_shake=0; /* _new_sake_pos == _current_shake_pos */
709 gl_update(sdl_tmpscreen->pixels);
710#endif
711}
712
713bool OSystem_SDL::show_mouse(bool visible) {
714 if (_mouse_visible == visible)
715 return visible;
716
717 bool last = _mouse_visible;
718 _mouse_visible = visible;
719
720 if (visible)
721 draw_mouse();
722 else
723 undraw_mouse();
724
725 return last;
726}
727
728void OSystem_SDL::set_mouse_pos(int x, int y) {
729 if (x != _ms_cur.x || y != _ms_cur.y) {
730 _ms_cur.x = x;
731 _ms_cur.y = y;
732 undraw_mouse();
733 }
734}
735
736void OSystem_SDL::set_mouse_cursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y) {
737 _ms_cur.w = w;
738 _ms_cur.h = h;
739
740 _ms_hotspot_x = hotspot_x;
741 _ms_hotspot_y = hotspot_y;
742
743 _ms_buf = (byte*)buf;
744
745 undraw_mouse();
746}
747
748void OSystem_SDL::set_shake_pos(int shake_pos) {
749 _new_shake_pos = shake_pos;
750}
751
752uint32 OSystem_SDL::get_msecs() {
753 return SDL_GetTicks();
754}
755
756void OSystem_SDL::delay_msecs(uint msecs) {
757 SDL_Delay(msecs);
758}
759
760void *OSystem_SDL::create_thread(ThreadProc *proc, void *param) {
761 return SDL_CreateThread(proc, param);
762}
763
764int mapKey(int key, byte mod)
765{
766 if (key >= SDLK_F1 && key <= SDLK_F9) {
767 return key - SDLK_F1 + 315;
768 } else if (key >= 'a' && key <= 'z' && mod & KMOD_SHIFT) {
769 key &= ~0x20;
770 } else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO)
771 return 0;
772 return key;
773}
774
775bool OSystem_SDL::poll_event(Event *event) {
776 SDL_Event ev;
777
778 for(;;) {
779 if (!SDL_PollEvent(&ev))
780 return false;
781
782 switch(ev.type) {
783 case SDL_KEYDOWN: {
784 byte b = 0;
785 if (ev.key.keysym.mod & KMOD_SHIFT) b |= KBD_SHIFT;
786 if (ev.key.keysym.mod & KMOD_CTRL) b |= KBD_CTRL;
787 if (ev.key.keysym.mod & KMOD_ALT) b |= KBD_ALT;
788 event->kbd.flags = b;
789
790 /* internal keypress? */
791 if (b == KBD_ALT && ev.key.keysym.sym==SDLK_RETURN) {
792 property(PROP_TOGGLE_FULLSCREEN, 0);
793 break;
794 }
795
796 if (b == KBD_CTRL && ev.key.keysym.sym=='z') {
797 quit();
798 break;
799 }
800
801 if (b == (KBD_CTRL|KBD_ALT) &&
802 ev.key.keysym.sym>='1' && ev.key.keysym.sym<='7') {
803 property(PROP_SET_GFX_MODE, ev.key.keysym.sym - '1');
804 break;
805 }
806
807
808 event->event_code = EVENT_KEYDOWN;
809 event->kbd.keycode = ev.key.keysym.sym;
810 event->kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod);
811 return true;
812 }
813
814 case SDL_MOUSEMOTION:
815 event->event_code = EVENT_MOUSEMOVE;
816 event->mouse.x = ev.motion.x;
817 event->mouse.y = ev.motion.y;
818
819 event->mouse.x /= scaling;
820 event->mouse.y /= scaling;
821
822 return true;
823
824 case SDL_MOUSEBUTTONDOWN:
825 if (ev.button.button == SDL_BUTTON_LEFT)
826 event->event_code = EVENT_LBUTTONDOWN;
827 else if (ev.button.button == SDL_BUTTON_RIGHT)
828 event->event_code = EVENT_RBUTTONDOWN;
829 else
830 break;
831 event->mouse.x = ev.button.x;
832 event->mouse.y = ev.button.y;
833 event->mouse.x /= scaling;
834 event->mouse.y /= scaling;
835
836 return true;
837
838 case SDL_MOUSEBUTTONUP:
839 if (ev.button.button == SDL_BUTTON_LEFT)
840 event->event_code = EVENT_LBUTTONUP;
841 else if (ev.button.button == SDL_BUTTON_RIGHT)
842 event->event_code = EVENT_RBUTTONUP;
843 else
844 break;
845 event->mouse.x = ev.button.x;
846 event->mouse.y = ev.button.y;
847 event->mouse.x /= scaling;
848 event->mouse.y /= scaling;
849 return true;
850
851 case SDL_QUIT:
852 quit();
853 }
854 }
855}
856
857bool OSystem_SDL::set_sound_proc(void *param, SoundProc *proc, byte format) {
858 SDL_AudioSpec desired;
859
860 /* only one format supported at the moment */
861
862 desired.freq = SAMPLES_PER_SEC;
863 desired.format = AUDIO_S16SYS;
864 desired.channels = 1;
865 desired.samples = 2048;
866 desired.callback = proc;
867 desired.userdata = param;
868 if (SDL_OpenAudio(&desired, NULL) != 0) {
869 return false;
870 }
871 SDL_PauseAudio(0);
872 return true;
873}
874
875
876/* retrieve the 320x200 bitmap currently being displayed */
877void OSystem_SDL::get_320x200_image(byte *buf) {
878 /* make sure the mouse is gone */
879 undraw_mouse();
880
881 if (SDL_LockSurface(sdl_screen) == -1)
882 error("SDL_LockSurface failed: %s.\n", SDL_GetError());
883
884 memcpy(buf, sdl_screen->pixels, 320*200);
885
886 SDL_UnlockSurface(sdl_screen);
887}
888
889void OSystem_SDL::hotswap_gfx_mode() {
890 /* hmm, need to allocate a 320x200 bitmap
891 * which will contain the "backup" of the screen during the change.
892 * then draw that to the new screen right after it's setup.
893 */
894
895 byte *bak_mem = (byte*)malloc(320*200);
896
897 get_320x200_image(bak_mem);
898
899 unload_gfx_mode();
900 load_gfx_mode();
901
902 force_full = true;
903
904#ifdef OPENGL
905 fb2gl_set_palette(0,256);
906 gl_update(sdl_tmpscreen->pixels);
907#else
908 /* reset palette */
909 SDL_SetColors(sdl_palscreen, _cur_pal, 0, 256);
910#endif
911
912 /* blit image */
913 OSystem_SDL::copy_rect(bak_mem, 320, 0, 0, 320, 200);
914 free(bak_mem);
915
916 OSystem_SDL::update_screen();
917}
918
919uint32 OSystem_SDL::property(int param, uint32 value) {
920 switch(param) {
921
922 case PROP_TOGGLE_FULLSCREEN:
923 _full_screen ^= true;
924 g_scumm->_fullScreen = _full_screen;
925
926 if (!SDL_WM_ToggleFullScreen(sdl_hwscreen)) {
927 /* if ToggleFullScreen fails, achieve the same effect with hotswap gfx mode */
928 hotswap_gfx_mode();
929 }
930 return 1;
931
932 case PROP_SET_WINDOW_CAPTION:
933 SDL_WM_SetCaption((char*)value, (char*)value);
934 return 1;
935
936 case PROP_OPEN_CD:
937 if (SDL_InitSubSystem(SDL_INIT_CDROM) == -1)
938 cdrom = NULL;
939 else {
940 cdrom = SDL_CDOpen(value);
941 /* Did if open? Check if cdrom is NULL */
942 if (!cdrom) {
943 warning("Couldn't open drive: %s\n", SDL_GetError());
944 }
945 }
946 break;
947
948 case PROP_SET_GFX_MODE:
949 if (value >= 7)
950 return 0;
951
952 _mode = value;
953 hotswap_gfx_mode();
954
955 return 1;
956
957 case PROP_SHOW_DEFAULT_CURSOR:
958 SDL_ShowCursor(value ? SDL_ENABLE : SDL_DISABLE);
959 break;
960
961 case PROP_GET_SAMPLE_RATE:
962 return SAMPLES_PER_SEC;
963 }
964
965 return 0;
966}
967
968void OSystem_SDL::quit() {
969 if(cdrom) {
970 SDL_CDStop(cdrom);
971 SDL_CDClose(cdrom);
972 }
973 unload_gfx_mode();
974 exit(1);
975}
976
977void OSystem_SDL::draw_mouse() {
978 if (_mouse_drawn || !_mouse_visible)
979 return;
980 _mouse_drawn = true;
981
982 if (SDL_LockSurface(sdl_screen) == -1)
983 error("SDL_LockSurface failed: %s.\n", SDL_GetError());
984
985 const int ydraw = _ms_cur.y - _ms_hotspot_y;
986 const int xdraw = _ms_cur.x - _ms_hotspot_x;
987 const int w = _ms_cur.w;
988 const int h = _ms_cur.h;
989 int x,y;
990 byte color;
991 byte *dst, *bak = _ms_backup;
992 byte *buf = _ms_buf;
993
994 _ms_old.w = w;
995 _ms_old.h = h;
996 _ms_old.x = xdraw;
997 _ms_old.y = ydraw;
998
999 dst = (byte *)sdl_screen->pixels + ydraw * 320 + xdraw;
1000
1001 for (y = 0; y < h; y++, dst += 320, bak += MAX_MOUSE_W, buf += w) {
1002 if ((uint) (ydraw + y) < 200) {
1003 for (x = 0; x < w; x++) {
1004 if ((uint) (xdraw + x) < 320) {
1005 bak[x] = dst[x];
1006 if ((color = buf[x]) != 0xFF) {
1007 dst[x] = color;
1008 }
1009 }
1010 }
1011 }
1012 }
1013
1014 add_dirty_rect(xdraw,ydraw,w,h);
1015
1016 SDL_UnlockSurface(sdl_screen);
1017}
1018
1019void OSystem_SDL::undraw_mouse() {
1020 if (!_mouse_drawn)
1021 return;
1022 _mouse_drawn = false;
1023
1024 if (SDL_LockSurface(sdl_screen) == -1)
1025 error("SDL_LockSurface failed: %s.\n", SDL_GetError());
1026
1027 byte *dst, *bak = _ms_backup;
1028 const int old_mouse_x = _ms_old.x;
1029 const int old_mouse_y = _ms_old.y;
1030 const int old_mouse_w = _ms_old.w;
1031 const int old_mouse_h = _ms_old.h;
1032 int x,y;
1033
1034 dst = (byte *)sdl_screen->pixels + old_mouse_y * 320 + old_mouse_x;
1035
1036 for (y = 0; y < old_mouse_h; y++, bak += MAX_MOUSE_W, dst += 320) {
1037 if ((uint) (old_mouse_y + y) < 200) {
1038 for (x = 0; x < old_mouse_w; x++) {
1039 if ((uint) (old_mouse_x + x) < 320) {
1040 dst[x] = bak[x];
1041 }
1042 }
1043 }
1044 }
1045
1046 add_dirty_rect(old_mouse_x, old_mouse_y, old_mouse_w, old_mouse_h);
1047
1048 SDL_UnlockSurface(sdl_screen);
1049}
1050
1051void OSystem_SDL::stop_cdrom() { /* Stop CD Audio in 1/10th of a second */
1052 cd_stop_time = SDL_GetTicks() + 100;
1053 cd_num_loops = 0;
1054
1055}
1056
1057void OSystem_SDL::play_cdrom(int track, int num_loops, int start_frame, int end_frame) {
1058 /* Reset sync count */
1059 g_scumm->_vars[g_scumm->VAR_MI1_TIMER] = 0;
1060
1061 if (!num_loops && !start_frame)
1062 return;
1063
1064 if (!cdrom)
1065 return;
1066
1067 if (end_frame > 0)
1068 end_frame+=5;
1069
1070 cd_track = track;
1071 cd_num_loops = num_loops;
1072 cd_start_frame = start_frame;
1073
1074 SDL_CDStatus(cdrom);
1075 SDL_CDPlayTracks(cdrom, track, start_frame, 0, end_frame);
1076 cd_end_frame = end_frame;
1077 cd_stop_time = 0;
1078 cd_end_time = SDL_GetTicks() + cdrom->track[track].length * 1000 / CD_FPS;
1079}
1080
1081bool OSystem_SDL::poll_cdrom() {
1082 if (!cdrom)
1083 return false;
1084
1085 return (cd_num_loops != 0 && (SDL_GetTicks() < cd_end_time || SDL_CDStatus(cdrom) != CD_STOPPED));
1086}
1087
1088void OSystem_SDL::update_cdrom() {
1089 if (!cdrom)
1090 return;
1091
1092 if (cd_stop_time != 0 && SDL_GetTicks() >= cd_stop_time) {
1093 SDL_CDStop(cdrom);
1094 cd_num_loops = 0;
1095 cd_stop_time = 0;
1096 return;
1097 }
1098
1099 if (cd_num_loops == 0 || SDL_GetTicks() < cd_end_time)
1100 return;
1101
1102 if (cd_num_loops != 1 && SDL_CDStatus(cdrom) != CD_STOPPED) {
1103 // Wait another second for it to be done
1104 cd_end_time += 1000;
1105 return;
1106 }
1107
1108 if (cd_num_loops > 0)
1109 cd_num_loops--;
1110
1111 if (cd_num_loops != 0) {
1112 SDL_CDPlayTracks(cdrom, cd_track, cd_start_frame, 0, cd_end_frame);
1113 cd_end_time = SDL_GetTicks() + cdrom->track[cd_track].length * 1000 / CD_FPS;
1114 }
1115}
1116
1117void OSystem_SDL::setup_icon() {
1118 int w, h, ncols, nbytes, i;
1119 unsigned int rgba[256], icon[32 * 32];
1120 unsigned char mask[32][4];
1121
1122 sscanf(scummvm_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes);
1123 if ((w != 32) || (h != 32) || (ncols > 255) || (nbytes > 1)) {
1124 warning("Could not load the icon (%d %d %d %d)", w, h, ncols, nbytes);
1125 return;
1126 }
1127 for (i = 0; i < ncols; i++) {
1128 unsigned char code;
1129 char color[32];
1130 unsigned int col;
1131 sscanf(scummvm_icon[1 + i], "%c c %s", &code, color);
1132 if (!strcmp(color, "None"))
1133 col = 0x00000000;
1134 else if (!strcmp(color, "black"))
1135 col = 0xFF000000;
1136 else if (color[0] == '#') {
1137 sscanf(color + 1, "%06x", &col);
1138 col |= 0xFF000000;
1139 } else {
1140 warning("Could not load the icon (%d %s - %s) ", code, color, scummvm_icon[1 + i]);
1141 return;
1142 }
1143
1144 rgba[code] = col;
1145 }
1146 memset(mask, 0, sizeof(mask));
1147 for (h = 0; h < 32; h++) {
1148 char *line = scummvm_icon[1 + ncols + h];
1149 for (w = 0; w < 32; w++) {
1150 icon[w + 32 * h] = rgba[line[w]];
1151 if (rgba[line[w]] & 0xFF000000) {
1152 mask[h][w >> 3] |= 1 << (7 - (w & 0x07));
1153 }
1154 }
1155 }
1156
1157 SDL_Surface *sdl_surf = SDL_CreateRGBSurfaceFrom(icon, 32, 32, 32, 32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
1158 SDL_WM_SetIcon(sdl_surf, (unsigned char *) mask);
1159}
1160
1161#ifdef USE_NULL_DRIVER
1162
1163/* NULL video driver */
1164class OSystem_NULL : public OSystem {
1165public:
1166 void set_palette(const byte *colors, uint start, uint num) {}
1167 void init_size(uint w, uint h);
1168 void copy_rect(const byte *buf, int pitch, int x, int y, int w, int h) {}
1169 void update_screen() {}
1170 bool show_mouse(bool visible) { return false; }
1171 void set_mouse_pos(int x, int y) {}
1172 void set_mouse_cursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y) {}
1173 void set_shake_pos(int shake_pos) {}
1174 uint32 get_msecs();
1175 void delay_msecs(uint msecs);
1176 void *create_thread(ThreadProc *proc, void *param) { return NULL; }
1177 bool poll_event(Event *event) { return false; }
1178 bool set_sound_proc(void *param, SoundProc *proc, byte sound) {}
1179 void quit() { exit(1); }
1180 uint32 property(int param, uint32 value) { return 0; }
1181 static OSystem *create(int gfx_mode, bool full_screen);
1182private:
1183
1184 uint msec_start;
1185
1186 uint32 get_ticks();
1187};
1188
1189void OSystem_NULL::init_size(uint w, uint h, byte sound) {
1190 msec_start = get_ticks();
1191}
1192
1193uint32 OSystem_NULL::get_ticks() {
1194 uint a = 0;
1195#ifdef WIN32
1196 a = GetTickCount();
1197#endif
1198
1199#ifdef UNIX
1200 struct timeval tv;
1201 gettimeofday(&tv, NULL);
1202 a = tv.tv_sec * 1000 + tv.tv_usec/1000;
1203#endif
1204
1205 return a;
1206}
1207
1208void OSystem_NULL::delay_msecs(uint msecs) {
1209#ifdef WIN32
1210 Sleep(msecs);
1211#endif
1212#ifdef UNIX
1213 usleep(msecs*1000);
1214#endif
1215}
1216
1217uint32 OSystem_NULL::get_msecs() {
1218 return get_ticks() - msec_start;
1219}
1220
1221OSystem *OSystem_NULL_create() {
1222 return new OSystem_NULL();
1223}
1224#else /* USE_NULL_DRIVER */
1225
1226OSystem *OSystem_NULL_create() {
1227 return NULL;
1228}
1229
1230#endif