Mercurial > sdl-ios-xcode
comparison src/video/ps2gs/SDL_gsvideo.c @ 70:f590dd383b5d
Added Linux PlayStation 2 Graphics Synthesizer support
author | Sam Lantinga <slouken@lokigames.com> |
---|---|
date | Sat, 16 Jun 2001 03:17:45 +0000 |
parents | |
children | 0bfcf0d2b874 |
comparison
equal
deleted
inserted
replaced
69:280ff3af2ecc | 70:f590dd383b5d |
---|---|
1 /* | |
2 SDL - Simple DirectMedia Layer | |
3 Copyright (C) 1997, 1998, 1999, 2000 Sam Lantinga | |
4 | |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 of the License, or (at your option) any later version. | |
9 | |
10 This library is distributed in the hope that it will be useful, | |
11 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
20 slouken@devolution.com | |
21 */ | |
22 | |
23 #ifdef SAVE_RCSID | |
24 static char rcsid = | |
25 "@(#) $Id$"; | |
26 #endif | |
27 | |
28 /* Framebuffer console based SDL video driver implementation. | |
29 */ | |
30 | |
31 #include <stdlib.h> | |
32 #include <stdio.h> | |
33 #include <fcntl.h> | |
34 #include <unistd.h> | |
35 #include <sys/ioctl.h> | |
36 #include <sys/mman.h> | |
37 | |
38 #include "SDL.h" | |
39 #include "SDL_error.h" | |
40 #include "SDL_video.h" | |
41 #include "SDL_mouse.h" | |
42 #include "SDL_sysvideo.h" | |
43 #include "SDL_pixels_c.h" | |
44 #include "SDL_events_c.h" | |
45 #include "SDL_cursor_c.h" | |
46 #include "SDL_gsvideo.h" | |
47 #include "SDL_gsmouse_c.h" | |
48 #include "SDL_gsevents_c.h" | |
49 #include "SDL_gsyuv_c.h" | |
50 | |
51 | |
52 /* Initialization/Query functions */ | |
53 static int GS_VideoInit(_THIS, SDL_PixelFormat *vformat); | |
54 static SDL_Rect **GS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); | |
55 static SDL_Surface *GS_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); | |
56 static int GS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors); | |
57 static void GS_VideoQuit(_THIS); | |
58 | |
59 /* Hardware surface functions */ | |
60 static int GS_AllocHWSurface(_THIS, SDL_Surface *surface); | |
61 static int GS_LockHWSurface(_THIS, SDL_Surface *surface); | |
62 static void GS_UnlockHWSurface(_THIS, SDL_Surface *surface); | |
63 static void GS_FreeHWSurface(_THIS, SDL_Surface *surface); | |
64 | |
65 /* GS driver bootstrap functions */ | |
66 | |
67 static int GS_Available(void) | |
68 { | |
69 int console, memory; | |
70 | |
71 console = open(PS2_DEV_GS, O_RDWR, 0); | |
72 if ( console >= 0 ) { | |
73 close(console); | |
74 } | |
75 memory = open(PS2_DEV_MEM, O_RDWR, 0); | |
76 if ( memory >= 0 ) { | |
77 close(memory); | |
78 } | |
79 return((console >= 0) && (memory >= 0)); | |
80 } | |
81 | |
82 static void GS_DeleteDevice(SDL_VideoDevice *device) | |
83 { | |
84 free(device->hidden); | |
85 free(device); | |
86 } | |
87 | |
88 static SDL_VideoDevice *GS_CreateDevice(int devindex) | |
89 { | |
90 SDL_VideoDevice *this; | |
91 | |
92 /* Initialize all variables that we clean on shutdown */ | |
93 this = (SDL_VideoDevice *)malloc(sizeof(SDL_VideoDevice)); | |
94 if ( this ) { | |
95 memset(this, 0, (sizeof *this)); | |
96 this->hidden = (struct SDL_PrivateVideoData *) | |
97 malloc((sizeof *this->hidden)); | |
98 } | |
99 if ( (this == NULL) || (this->hidden == NULL) ) { | |
100 SDL_OutOfMemory(); | |
101 if ( this ) { | |
102 free(this); | |
103 } | |
104 return(0); | |
105 } | |
106 memset(this->hidden, 0, (sizeof *this->hidden)); | |
107 mouse_fd = -1; | |
108 keyboard_fd = -1; | |
109 | |
110 /* Set the function pointers */ | |
111 this->VideoInit = GS_VideoInit; | |
112 this->ListModes = GS_ListModes; | |
113 this->SetVideoMode = GS_SetVideoMode; | |
114 this->CreateYUVOverlay = GS_CreateYUVOverlay; | |
115 this->SetColors = GS_SetColors; | |
116 this->UpdateRects = NULL; | |
117 this->VideoQuit = GS_VideoQuit; | |
118 this->AllocHWSurface = GS_AllocHWSurface; | |
119 this->CheckHWBlit = NULL; | |
120 this->FillHWRect = NULL; | |
121 this->SetHWColorKey = NULL; | |
122 this->SetHWAlpha = NULL; | |
123 this->LockHWSurface = GS_LockHWSurface; | |
124 this->UnlockHWSurface = GS_UnlockHWSurface; | |
125 this->FlipHWSurface = NULL; | |
126 this->FreeHWSurface = GS_FreeHWSurface; | |
127 this->SetIcon = NULL; | |
128 this->SetCaption = NULL; | |
129 this->GetWMInfo = NULL; | |
130 this->FreeWMCursor = GS_FreeWMCursor; | |
131 this->CreateWMCursor = GS_CreateWMCursor; | |
132 this->ShowWMCursor = GS_ShowWMCursor; | |
133 this->MoveWMCursor = GS_MoveWMCursor; | |
134 this->InitOSKeymap = GS_InitOSKeymap; | |
135 this->PumpEvents = GS_PumpEvents; | |
136 | |
137 this->free = GS_DeleteDevice; | |
138 | |
139 return this; | |
140 } | |
141 | |
142 VideoBootStrap PS2GS_bootstrap = { | |
143 "ps2gs", "PlayStation 2 Graphics Synthesizer", | |
144 GS_Available, GS_CreateDevice | |
145 }; | |
146 | |
147 /* These are the pixel formats for the 32, 24, and 16 bit video modes */ | |
148 static struct { | |
149 int bpp; | |
150 Uint32 r; | |
151 Uint32 g; | |
152 Uint32 b; | |
153 } GS_pixelmasks[] = { | |
154 { 32, 0x000000FF, /* RGB little-endian */ | |
155 0x0000FF00, | |
156 0x00FF0000 }, | |
157 { 24, 0x000000FF, /* RGB little-endian */ | |
158 0x0000FF00, | |
159 0x00FF0000 }, | |
160 { 16, 0x0000001f, /* RGB little-endian */ | |
161 0x000003e0, | |
162 0x00007c00 }, | |
163 }; | |
164 /* This is a mapping from SDL bytes-per-pixel to GS pixel format */ | |
165 static int GS_formatmap[] = { | |
166 -1, /* 0 bpp, not a legal value */ | |
167 -1, /* 8 bpp, not supported (yet?) */ | |
168 PS2_GS_PSMCT16, /* 16 bpp */ | |
169 PS2_GS_PSMCT24, /* 24 bpp */ | |
170 PS2_GS_PSMCT32 /* 32 bpp */ | |
171 }; | |
172 | |
173 static unsigned long long head_tags[] __attribute__((aligned(16))) = { | |
174 4 | (1LL << 60), /* GIFtag */ | |
175 0x0e, /* A+D */ | |
176 0, /* 2 */ | |
177 PS2_GS_BITBLTBUF, | |
178 0, /* 4 */ | |
179 PS2_GS_TRXPOS, | |
180 0, /* 6 */ | |
181 PS2_GS_TRXREG, | |
182 0, /* 8 */ | |
183 PS2_GS_TRXDIR | |
184 }; | |
185 | |
186 #define MAXIMG (32767 * 16) | |
187 #define MAXTAGS 8 | |
188 | |
189 static inline int loadimage_nonblock(int fd, struct ps2_image *image, int size, | |
190 unsigned long long *hm, | |
191 unsigned long long *im) | |
192 { | |
193 struct ps2_plist plist; | |
194 struct ps2_packet packet[1 + MAXTAGS * 2]; | |
195 int isize; | |
196 int pnum, it, eop; | |
197 char *data; | |
198 | |
199 /* initialize the variables */ | |
200 data = (char *)image->ptr; | |
201 pnum = it = eop = 0; | |
202 plist.packet = packet; | |
203 | |
204 /* make BITBLT packet */ | |
205 packet[pnum].ptr = hm; | |
206 packet[pnum].len = sizeof(head_tags); | |
207 pnum++; | |
208 hm[2] = ((unsigned long long)image->fbp << 32) | | |
209 ((unsigned long long)image->fbw << 48) | | |
210 ((unsigned long long)image->psm << 56); | |
211 hm[4] = ((unsigned long long)image->x << 32) | | |
212 ((unsigned long long)image->y << 48); | |
213 hm[6] = (unsigned long long)image->w | | |
214 ((unsigned long long)image->h << 32); | |
215 | |
216 /* make image mode tags */ | |
217 while (!eop) { | |
218 isize = size > MAXIMG ? MAXIMG : size; | |
219 size -= isize; | |
220 eop = (size == 0); | |
221 | |
222 packet[pnum].ptr = &im[it]; | |
223 packet[pnum].len = sizeof(unsigned long long) * 2; | |
224 pnum++; | |
225 im[it++] = (isize >> 4) | (eop ? (1 << 15) : 0) | (2LL << 58); | |
226 im[it++] = 0; | |
227 | |
228 packet[pnum].ptr = (void *)data; | |
229 packet[pnum].len = isize; | |
230 pnum++; | |
231 data += isize; | |
232 } | |
233 plist.num = pnum; | |
234 | |
235 return ioctl(fd, PS2IOC_SENDL, &plist); | |
236 } | |
237 | |
238 static int GS_VideoInit(_THIS, SDL_PixelFormat *vformat) | |
239 { | |
240 struct ps2_screeninfo vinfo; | |
241 | |
242 /* Initialize the library */ | |
243 console_fd = open(PS2_DEV_GS, O_RDWR, 0); | |
244 if ( console_fd < 0 ) { | |
245 SDL_SetError("Unable to open %s", PS2_DEV_GS); | |
246 return(-1); | |
247 } | |
248 memory_fd = open(PS2_DEV_MEM, O_RDWR, 0); | |
249 if ( memory_fd < 0 ) { | |
250 close(console_fd); | |
251 console_fd = -1; | |
252 SDL_SetError("Unable to open %s", PS2_DEV_MEM); | |
253 return(-1); | |
254 } | |
255 | |
256 /* Determine the current screen depth */ | |
257 if ( ioctl(console_fd, PS2IOC_GSCREENINFO, &vinfo) < 0 ) { | |
258 close(memory_fd); | |
259 close(console_fd); | |
260 console_fd = -1; | |
261 SDL_SetError("Couldn't get console pixel format"); | |
262 return(-1); | |
263 } | |
264 if ( vinfo.mode != PS2_GS_VESA ) { | |
265 GS_VideoQuit(this); | |
266 SDL_SetError("Console must be in VESA video mode"); | |
267 return(-1); | |
268 } | |
269 switch (vinfo.psm) { | |
270 /* Supported pixel formats */ | |
271 case PS2_GS_PSMCT32: | |
272 case PS2_GS_PSMCT24: | |
273 case PS2_GS_PSMCT16: | |
274 break; | |
275 default: | |
276 GS_VideoQuit(this); | |
277 SDL_SetError("Unknown console pixel format: %d", vinfo.psm); | |
278 return(-1); | |
279 } | |
280 vformat->BitsPerPixel = GS_pixelmasks[vinfo.psm].bpp; | |
281 vformat->Rmask = GS_pixelmasks[vinfo.psm].r; | |
282 vformat->Gmask = GS_pixelmasks[vinfo.psm].g; | |
283 vformat->Bmask = GS_pixelmasks[vinfo.psm].b; | |
284 saved_vinfo = vinfo; | |
285 | |
286 /* Enable mouse and keyboard support */ | |
287 if ( GS_OpenKeyboard(this) < 0 ) { | |
288 GS_VideoQuit(this); | |
289 SDL_SetError("Unable to open keyboard"); | |
290 return(-1); | |
291 } | |
292 if ( GS_OpenMouse(this) < 0 ) { | |
293 const char *sdl_nomouse; | |
294 | |
295 sdl_nomouse = getenv("SDL_NOMOUSE"); | |
296 if ( ! sdl_nomouse ) { | |
297 GS_VideoQuit(this); | |
298 SDL_SetError("Unable to open mouse"); | |
299 return(-1); | |
300 } | |
301 } | |
302 | |
303 /* We're done! */ | |
304 return(0); | |
305 } | |
306 | |
307 static SDL_Rect **GS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) | |
308 { | |
309 static SDL_Rect GS_mode_list[] = { | |
310 { 0, 0, 1280, 1024 }, | |
311 { 0, 0, 1024, 768 }, | |
312 { 0, 0, 800, 600 }, | |
313 { 0, 0, 640, 480 } | |
314 }; | |
315 static SDL_Rect *GS_modes[] = { | |
316 &GS_mode_list[0], | |
317 &GS_mode_list[1], | |
318 &GS_mode_list[2], | |
319 &GS_mode_list[3], | |
320 NULL | |
321 }; | |
322 SDL_Rect **modes; | |
323 | |
324 switch (format->BitsPerPixel) { | |
325 case 16: | |
326 case 24: | |
327 case 32: | |
328 modes = GS_modes; | |
329 break; | |
330 default: | |
331 modes = NULL; | |
332 break; | |
333 } | |
334 return(modes); | |
335 } | |
336 | |
337 /* Various screen update functions available */ | |
338 static void GS_DMAFullUpdate(_THIS, int numrects, SDL_Rect *rects); | |
339 | |
340 static SDL_Surface *GS_SetVideoMode(_THIS, SDL_Surface *current, | |
341 int width, int height, int bpp, Uint32 flags) | |
342 { | |
343 struct ps2_screeninfo vinfo; | |
344 | |
345 /* Set the terminal into graphics mode */ | |
346 if ( GS_EnterGraphicsMode(this) < 0 ) { | |
347 return(NULL); | |
348 } | |
349 | |
350 /* Set the video mode and get the final screen format */ | |
351 if ( ioctl(console_fd, PS2IOC_GSCREENINFO, &vinfo) < 0 ) { | |
352 SDL_SetError("Couldn't get console screen info"); | |
353 return(NULL); | |
354 } | |
355 if ( (vinfo.w != width) || (vinfo.h != height) || | |
356 (GS_pixelmasks[vinfo.psm].bpp != bpp) ) { | |
357 switch (width) { | |
358 case 640: | |
359 vinfo.res = PS2_GS_640x480; | |
360 break; | |
361 case 800: | |
362 vinfo.res = PS2_GS_800x600; | |
363 break; | |
364 case 1024: | |
365 vinfo.res = PS2_GS_1024x768; | |
366 break; | |
367 case 1280: | |
368 vinfo.res = PS2_GS_1280x1024; | |
369 break; | |
370 default: | |
371 SDL_SetError("Unsupported resolution: %dx%d\n", | |
372 width, height); | |
373 return(NULL); | |
374 } | |
375 vinfo.res |= (PS2_GS_75Hz << 8); | |
376 vinfo.w = width; | |
377 vinfo.h = height; | |
378 vinfo.fbp = 0; | |
379 vinfo.psm = GS_formatmap[bpp/8]; | |
380 if ( vinfo.psm < 0 ) { | |
381 SDL_SetError("Unsupported depth: %d bpp\n", bpp); | |
382 return(NULL); | |
383 } | |
384 if ( ioctl(console_fd, PS2IOC_SSCREENINFO, &vinfo) < 0 ) { | |
385 SDL_SetError("Couldn't set console screen info"); | |
386 return(NULL); | |
387 } | |
388 | |
389 /* Unmap the previous DMA buffer */ | |
390 if ( mapped_mem ) { | |
391 munmap(mapped_mem, mapped_len); | |
392 mapped_mem = NULL; | |
393 } | |
394 } | |
395 if ( ! SDL_ReallocFormat(current, GS_pixelmasks[vinfo.psm].bpp, | |
396 GS_pixelmasks[vinfo.psm].r, | |
397 GS_pixelmasks[vinfo.psm].g, | |
398 GS_pixelmasks[vinfo.psm].b, 0) ) { | |
399 return(NULL); | |
400 } | |
401 | |
402 /* Set up the new mode framebuffer */ | |
403 current->flags = SDL_FULLSCREEN; | |
404 current->w = vinfo.w; | |
405 current->h = vinfo.h; | |
406 current->pitch = SDL_CalculatePitch(current); | |
407 | |
408 /* Memory map the DMA area for block memory transfer */ | |
409 if ( ! mapped_mem ) { | |
410 pixels_len = height * current->pitch; | |
411 mapped_len = pixels_len + | |
412 /* Screen update DMA command area */ | |
413 sizeof(head_tags) + ((2 * MAXTAGS) * 16); | |
414 mapped_mem = mmap(0, mapped_len, PROT_READ|PROT_WRITE, | |
415 MAP_SHARED, memory_fd, 0); | |
416 if ( mapped_mem == MAP_FAILED ) { | |
417 SDL_SetError("Unable to map %d bytes for DMA", | |
418 mapped_len); | |
419 mapped_mem = NULL; | |
420 return(NULL); | |
421 } | |
422 | |
423 /* Set up the entire screen for DMA transfer */ | |
424 screen_image.ptr = mapped_mem; | |
425 screen_image.fbp = 0; | |
426 screen_image.fbw = (vinfo.w + 63) / 64; | |
427 screen_image.psm = vinfo.psm; | |
428 screen_image.x = 0; | |
429 screen_image.y = 0; | |
430 screen_image.w = vinfo.w; | |
431 screen_image.h = vinfo.h; | |
432 | |
433 /* get screen image data size (qword aligned) */ | |
434 screen_image_size = (vinfo.w * vinfo.h); | |
435 switch (screen_image.psm) { | |
436 case PS2_GS_PSMCT32: | |
437 screen_image_size *= 4; | |
438 break; | |
439 case PS2_GS_PSMCT24: | |
440 screen_image_size *= 3; | |
441 break; | |
442 case PS2_GS_PSMCT16: | |
443 screen_image_size *= 2; | |
444 break; | |
445 } | |
446 screen_image_size = (screen_image_size + 15) & ~15; | |
447 | |
448 /* Set up the memory for screen update DMA commands */ | |
449 head_tags_mem = (unsigned long long *) | |
450 (mapped_mem + pixels_len); | |
451 image_tags_mem = (unsigned long long *) | |
452 ((caddr_t)head_tags_mem + sizeof(head_tags)); | |
453 memcpy(head_tags_mem, head_tags, sizeof(head_tags)); | |
454 } | |
455 current->pixels = NULL; | |
456 if ( getenv("SDL_FULLSCREEN_UPDATE") ) { | |
457 /* Correct semantics */ | |
458 current->flags |= SDL_ASYNCBLIT; | |
459 } else { | |
460 /* We lie here - the screen memory isn't really the visible | |
461 display memory and still requires an update, but this | |
462 has the desired effect for most applications. | |
463 */ | |
464 current->flags |= SDL_HWSURFACE; | |
465 } | |
466 | |
467 /* Set the update rectangle function */ | |
468 this->UpdateRects = GS_DMAFullUpdate; | |
469 | |
470 /* We're done */ | |
471 return(current); | |
472 } | |
473 | |
474 /* We don't support hardware surfaces yet */ | |
475 static int GS_AllocHWSurface(_THIS, SDL_Surface *surface) | |
476 { | |
477 return(-1); | |
478 } | |
479 static void GS_FreeHWSurface(_THIS, SDL_Surface *surface) | |
480 { | |
481 return; | |
482 } | |
483 static int GS_LockHWSurface(_THIS, SDL_Surface *surface) | |
484 { | |
485 if ( surface == this->screen ) { | |
486 /* Since mouse motion affects 'pixels', lock it */ | |
487 SDL_LockCursor(); | |
488 | |
489 /* Make sure any pending DMA has completed */ | |
490 if ( dma_pending ) { | |
491 ioctl(console_fd, PS2IOC_SENDQCT, 1); | |
492 dma_pending = 0; | |
493 } | |
494 | |
495 /* If the cursor is drawn on the DMA area, remove it */ | |
496 if ( cursor_drawn ) { | |
497 surface->pixels = mapped_mem + surface->offset; | |
498 SDL_EraseCursorNoLock(this->screen); | |
499 cursor_drawn = 0; | |
500 } | |
501 | |
502 /* Set the surface pixels to the base of the DMA area */ | |
503 surface->pixels = mapped_mem; | |
504 | |
505 /* We're finished! */ | |
506 SDL_UnlockCursor(); | |
507 } | |
508 return(0); | |
509 } | |
510 static void GS_UnlockHWSurface(_THIS, SDL_Surface *surface) | |
511 { | |
512 if ( surface == this->screen ) { | |
513 /* Since mouse motion affects 'pixels', lock it */ | |
514 SDL_LockCursor(); | |
515 surface->pixels = NULL; | |
516 SDL_UnlockCursor(); | |
517 } | |
518 } | |
519 | |
520 static void GS_DMAFullUpdate(_THIS, int numrects, SDL_Rect *rects) | |
521 { | |
522 /* Lock so we aren't interrupted by a mouse update */ | |
523 SDL_LockCursor(); | |
524 | |
525 /* Make sure any pending DMA has completed */ | |
526 if ( dma_pending ) { | |
527 ioctl(console_fd, PS2IOC_SENDQCT, 1); | |
528 dma_pending = 0; | |
529 } | |
530 | |
531 /* If the mouse is visible, draw it on the DMA area */ | |
532 if ( (SDL_cursorstate & CURSOR_VISIBLE) && !cursor_drawn ) { | |
533 this->screen->pixels = mapped_mem + this->screen->offset; | |
534 SDL_DrawCursorNoLock(this->screen); | |
535 this->screen->pixels = NULL; | |
536 cursor_drawn = 1; | |
537 } | |
538 | |
539 /* Put the image onto the screen */ | |
540 loadimage_nonblock(console_fd, | |
541 &screen_image, screen_image_size, | |
542 head_tags_mem, image_tags_mem); | |
543 dma_pending = 1; | |
544 | |
545 /* We're finished! */ | |
546 SDL_UnlockCursor(); | |
547 } | |
548 | |
549 static int GS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) | |
550 { | |
551 return(0); | |
552 } | |
553 | |
554 static void GS_VideoQuit(_THIS) | |
555 { | |
556 /* Close console and input file descriptors */ | |
557 if ( console_fd > 0 ) { | |
558 /* Unmap the video framebuffer */ | |
559 if ( mapped_mem ) { | |
560 /* Unmap the video framebuffer */ | |
561 munmap(mapped_mem, mapped_len); | |
562 mapped_mem = NULL; | |
563 } | |
564 close(memory_fd); | |
565 | |
566 /* Restore the original video mode */ | |
567 if ( GS_InGraphicsMode(this) ) { | |
568 ioctl(console_fd, PS2IOC_SSCREENINFO, &saved_vinfo); | |
569 } | |
570 | |
571 /* We're all done with the graphics device */ | |
572 close(console_fd); | |
573 console_fd = -1; | |
574 } | |
575 GS_CloseMouse(this); | |
576 GS_CloseKeyboard(this); | |
577 } |