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