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#include <sys/time.h> 25#include <sys/mman.h> 26#include <sys/ioctl.h> 27#include <dev/wscons/wsdisplay_usl_io.h> 28#include <fcntl.h> 29#include <unistd.h> 30#include <errno.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 38#include "SDL_wsconsvideo.h" 39#include "SDL_wsconsevents_c.h" 40#include "SDL_wsconsmouse_c.h" 41 42#define WSCONSVID_DRIVER_NAME "wscons" 43enum { 44 WSCONS_ROTATE_NONE = 0, 45 WSCONS_ROTATE_CCW = 90, 46 WSCONS_ROTATE_UD = 180, 47 WSCONS_ROTATE_CW = 270 48}; 49 50#define min(a,b) ((a)<(b)?(a):(b)) 51 52/* Initialization/Query functions */ 53static int WSCONS_VideoInit(_THIS, SDL_PixelFormat *vformat); 54static SDL_Rect **WSCONS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); 55static SDL_Surface *WSCONS_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); 56static int WSCONS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors); 57static void WSCONS_VideoQuit(_THIS); 58 59/* Hardware surface functions */ 60static int WSCONS_AllocHWSurface(_THIS, SDL_Surface *surface); 61static int WSCONS_LockHWSurface(_THIS, SDL_Surface *surface); 62static void WSCONS_UnlockHWSurface(_THIS, SDL_Surface *surface); 63static void WSCONS_FreeHWSurface(_THIS, SDL_Surface *surface); 64 65/* etc. */ 66static WSCONS_bitBlit WSCONS_blit16; 67static WSCONS_bitBlit WSCONS_blit16blocked; 68static void WSCONS_UpdateRects(_THIS, int numrects, SDL_Rect *rects); 69 70void WSCONS_ReportError(char *fmt, ...) 71{ 72 char message[200]; 73 va_list vaArgs; 74 75 message[199] = '\0'; 76 77 va_start(vaArgs, fmt); 78 vsnprintf(message, 199, fmt, vaArgs); 79 va_end(vaArgs); 80 81 SDL_SetError(message); 82 fprintf(stderr, "WSCONS error: %s\n", message); 83} 84 85/* WSCONS driver bootstrap functions */ 86 87static int WSCONS_Available(void) 88{ 89 return 1; 90} 91 92static void WSCONS_DeleteDevice(SDL_VideoDevice *device) 93{ 94 SDL_free(device->hidden); 95 SDL_free(device); 96} 97 98static SDL_VideoDevice *WSCONS_CreateDevice(int devindex) 99{ 100 SDL_VideoDevice *device; 101 102 /* Initialize all variables that we clean on shutdown */ 103 device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice)); 104 if (device == NULL) { 105 SDL_OutOfMemory(); 106 return 0; 107 } 108 SDL_memset(device, 0, (sizeof *device)); 109 device->hidden = 110 (struct SDL_PrivateVideoData *)SDL_malloc((sizeof *device->hidden)); 111 if (device->hidden == NULL) { 112 SDL_OutOfMemory(); 113 SDL_free(device); 114 return(0); 115 } 116 SDL_memset(device->hidden, 0, (sizeof *device->hidden)); 117 device->hidden->fd = -1; 118 119 /* Set the function pointers */ 120 device->VideoInit = WSCONS_VideoInit; 121 device->ListModes = WSCONS_ListModes; 122 device->SetVideoMode = WSCONS_SetVideoMode; 123 device->SetColors = WSCONS_SetColors; 124 device->UpdateRects = WSCONS_UpdateRects; 125 device->VideoQuit = WSCONS_VideoQuit; 126 device->AllocHWSurface = WSCONS_AllocHWSurface; 127 device->LockHWSurface = WSCONS_LockHWSurface; 128 device->UnlockHWSurface = WSCONS_UnlockHWSurface; 129 device->FreeHWSurface = WSCONS_FreeHWSurface; 130 device->InitOSKeymap = WSCONS_InitOSKeymap; 131 device->PumpEvents = WSCONS_PumpEvents; 132 device->free = WSCONS_DeleteDevice; 133 134 return device; 135} 136 137VideoBootStrap WSCONS_bootstrap = { 138 WSCONSVID_DRIVER_NAME, 139 "SDL wscons video driver", 140 WSCONS_Available, 141 WSCONS_CreateDevice 142}; 143 144#define WSCONSDEV_FORMAT "/dev/ttyC%01x" 145 146int WSCONS_VideoInit(_THIS, SDL_PixelFormat *vformat) 147{ 148 char devnamebuf[30]; 149 char *devname; 150 char *rotation; 151 int wstype; 152 int wsmode = WSDISPLAYIO_MODE_DUMBFB; 153 size_t len, mapsize; 154 int pagemask; 155 int width, height; 156 157 devname = SDL_getenv("SDL_WSCONSDEV"); 158 if (devname == NULL) { 159 int activeVT; 160 if (ioctl(STDIN_FILENO, VT_GETACTIVE, &activeVT) == -1) { 161 WSCONS_ReportError("Unable to determine active terminal: %s", 162 strerror(errno)); 163 return -1; 164 } 165 SDL_snprintf(devnamebuf, sizeof(devnamebuf), WSCONSDEV_FORMAT, activeVT - 1); 166 devname = devnamebuf; 167 } 168 169 private->fd = open(devname, O_RDWR | O_NONBLOCK, 0); 170 if (private->fd == -1) { 171 WSCONS_ReportError("open %s: %s", devname, strerror(errno)); 172 return -1; 173 } 174 if (ioctl(private->fd, WSDISPLAYIO_GINFO, &private->info) == -1) { 175 WSCONS_ReportError("ioctl WSDISPLAY_GINFO: %s", strerror(errno)); 176 return -1; 177 } 178 if (ioctl(private->fd, WSDISPLAYIO_GTYPE, &wstype) == -1) { 179 WSCONS_ReportError("ioctl WSDISPLAY_GTYPE: %s", strerror(errno)); 180 return -1; 181 } 182 if (ioctl(private->fd, WSDISPLAYIO_LINEBYTES, &private->physlinebytes) == -1) { 183 WSCONS_ReportError("ioctl WSDISPLAYIO_LINEBYTES: %s", strerror(errno)); 184 return -1; 185 } 186 if (private->info.depth > 8) { 187 if (wstype == WSDISPLAY_TYPE_SUN24 || 188 wstype == WSDISPLAY_TYPE_SUNCG12 || 189 wstype == WSDISPLAY_TYPE_SUNCG14 || 190 wstype == WSDISPLAY_TYPE_SUNTCX || 191 wstype == WSDISPLAY_TYPE_SUNFFB) { 192 private->redMask = 0x0000ff; 193 private->greenMask = 0x00ff00; 194 private->blueMask = 0xff0000; 195#ifdef WSDISPLAY_TYPE_PXALCD 196 } else if (wstype == WSDISPLAY_TYPE_PXALCD) { 197 private->redMask = 0x1f << 11; 198 private->greenMask = 0x3f << 5; 199 private->blueMask = 0x1f; 200#endif 201 } else { 202 WSCONS_ReportError("Unknown video hardware"); 203 return -1; 204 } 205 } else { 206 WSCONS_ReportError("Displays with 8 bpp or less are not supported"); 207 return -1; 208 } 209 210 private->rotate = WSCONS_ROTATE_NONE; 211 rotation = SDL_getenv("SDL_VIDEO_WSCONS_ROTATION"); 212 if (rotation != NULL) { 213 if (SDL_strlen(rotation) == 0) { 214 private->shadowFB = 0; 215 private->rotate = WSCONS_ROTATE_NONE; 216 printf("Not rotating, no shadow\n"); 217 } else if (!SDL_strcmp(rotation, "NONE")) { 218 private->shadowFB = 1; 219 private->rotate = WSCONS_ROTATE_NONE; 220 printf("Not rotating, but still using shadow\n"); 221 } else if (!SDL_strcmp(rotation, "CW")) { 222 private->shadowFB = 1; 223 private->rotate = WSCONS_ROTATE_CW; 224 printf("Rotating screen clockwise\n"); 225 } else if (!SDL_strcmp(rotation, "CCW")) { 226 private->shadowFB = 1; 227 private->rotate = WSCONS_ROTATE_CCW; 228 printf("Rotating screen counter clockwise\n"); 229 } else if (!SDL_strcmp(rotation, "UD")) { 230 private->shadowFB = 1; 231 private->rotate = WSCONS_ROTATE_UD; 232 printf("Rotating screen upside down\n"); 233 } else { 234 WSCONS_ReportError("\"%s\" is not a valid value for " 235 "SDL_VIDEO_WSCONS_ROTATION", rotation); 236 return -1; 237 } 238 } 239 240 switch (private->info.depth) { 241 case 1: 242 case 4: 243 case 8: 244 len = private->physlinebytes * private->info.height; 245 break; 246 case 16: 247 if (private->physlinebytes == private->info.width) { 248 len = private->info.width * private->info.height * sizeof(short); 249 } else { 250 len = private->physlinebytes * private->info.height; 251 } 252 if (private->rotate == WSCONS_ROTATE_NONE || 253 private->rotate == WSCONS_ROTATE_UD) { 254 private->blitFunc = WSCONS_blit16; 255 } else { 256 private->blitFunc = WSCONS_blit16blocked; 257 } 258 break; 259 case 32: 260 if (private->physlinebytes == private->info.width) { 261 len = private->info.width * private->info.height * sizeof(int); 262 } else { 263 len = private->physlinebytes * private->info.height; 264 } 265 break; 266 default: 267 WSCONS_ReportError("unsupported depth %d", private->info.depth); 268 return -1; 269 } 270 271 if (private->shadowFB && private->blitFunc == NULL) { 272 WSCONS_ReportError("Using software buffer, but no blitter function is " 273 "available for this %d bpp.", private->info.depth); 274 return -1; 275 } 276 277 if (ioctl(private->fd, WSDISPLAYIO_SMODE, &wsmode) == -1) { 278 WSCONS_ReportError("ioctl SMODE"); 279 return -1; 280 } 281 282 pagemask = getpagesize() - 1; 283 mapsize = ((int)len + pagemask) & ~pagemask; 284 private->physmem = (Uint8 *)mmap(NULL, mapsize, 285 PROT_READ | PROT_WRITE, MAP_SHARED, 286 private->fd, (off_t)0); 287 if (private->physmem == (Uint8 *)MAP_FAILED) { 288 private->physmem = NULL; 289 WSCONS_ReportError("mmap: %s", strerror(errno)); 290 return -1; 291 } 292 private->fbmem_len = len; 293 294 if (private->rotate == WSCONS_ROTATE_CW || 295 private->rotate == WSCONS_ROTATE_CCW) { 296 width = private->info.height; 297 height = private->info.width; 298 } else { 299 width = private->info.width; 300 height = private->info.height; 301 } 302 303 this->info.current_w = width; 304 this->info.current_h = height; 305 306 if (private->shadowFB) { 307 private->shadowmem = (Uint8 *)SDL_malloc(len); 308 if (private->shadowmem == NULL) { 309 WSCONS_ReportError("No memory for shadow"); 310 return -1; 311 } 312 private->fbstart = private->shadowmem; 313 private->fblinebytes = width * ((private->info.depth + 7) / 8); 314 } else { 315 private->fbstart = private->physmem; 316 private->fblinebytes = private->physlinebytes; 317 } 318 319 private->SDL_modelist[0] = (SDL_Rect *)SDL_malloc(sizeof(SDL_Rect)); 320 private->SDL_modelist[0]->w = width; 321 private->SDL_modelist[0]->h = height; 322 323 vformat->BitsPerPixel = private->info.depth; 324 vformat->BytesPerPixel = private->info.depth / 8; 325 326 if (WSCONS_InitKeyboard(this) == -1) { 327 return -1; 328 } 329 330 return 0; 331} 332 333SDL_Rect **WSCONS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) 334{ 335 if (format->BitsPerPixel == private->info.depth) { 336 return private->SDL_modelist; 337 } else { 338 return NULL; 339 } 340} 341 342SDL_Surface *WSCONS_SetVideoMode(_THIS, SDL_Surface *current, 343 int width, int height, int bpp, Uint32 flags) 344{ 345 if (width != private->SDL_modelist[0]->w || 346 height != private->SDL_modelist[0]->h) { 347 WSCONS_ReportError("Requested video mode %dx%d not supported.", 348 width, height); 349 return NULL; 350 } 351 if (bpp != private->info.depth) { 352 WSCONS_ReportError("Requested video depth %d bpp not supported.", bpp); 353 return NULL; 354 } 355 356 if (!SDL_ReallocFormat(current, 357 bpp, 358 private->redMask, 359 private->greenMask, 360 private->blueMask, 361 0)) { 362 WSCONS_ReportError("Couldn't allocate new pixel format"); 363 return NULL; 364 } 365 366 current->flags &= SDL_FULLSCREEN; 367 if (private->shadowFB) { 368 current->flags |= SDL_SWSURFACE; 369 } else { 370 current->flags |= SDL_HWSURFACE; 371 } 372 current->w = width; 373 current->h = height; 374 current->pitch = private->fblinebytes; 375 current->pixels = private->fbstart; 376 377 SDL_memset(private->fbstart, 0, private->fbmem_len); 378 379 return current; 380} 381 382static int WSCONS_AllocHWSurface(_THIS, SDL_Surface *surface) 383{ 384 return -1; 385} 386static void WSCONS_FreeHWSurface(_THIS, SDL_Surface *surface) 387{ 388} 389 390static int WSCONS_LockHWSurface(_THIS, SDL_Surface *surface) 391{ 392 return 0; 393} 394 395static void WSCONS_UnlockHWSurface(_THIS, SDL_Surface *surface) 396{ 397} 398 399static void WSCONS_blit16(Uint8 *byte_src_pos, 400 int srcRightDelta, 401 int srcDownDelta, 402 Uint8 *byte_dst_pos, 403 int dst_linebytes, 404 int width, 405 int height) 406{ 407 int w; 408 Uint16 *src_pos = (Uint16 *)byte_src_pos; 409 Uint16 *dst_pos = (Uint16 *)byte_dst_pos; 410 411 while (height) { 412 Uint16 *src = src_pos; 413 Uint16 *dst = dst_pos; 414 for (w = width; w != 0; w--) { 415 *dst = *src; 416 src += srcRightDelta; 417 dst++; 418 } 419 dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes); 420 src_pos += srcDownDelta; 421 height--; 422 } 423} 424 425#define BLOCKSIZE_W 32 426#define BLOCKSIZE_H 32 427 428static void WSCONS_blit16blocked(Uint8 *byte_src_pos, 429 int srcRightDelta, 430 int srcDownDelta, 431 Uint8 *byte_dst_pos, 432 int dst_linebytes, 433 int width, 434 int height) 435{ 436 int w; 437 Uint16 *src_pos = (Uint16 *)byte_src_pos; 438 Uint16 *dst_pos = (Uint16 *)byte_dst_pos; 439 440 while (height > 0) { 441 Uint16 *src = src_pos; 442 Uint16 *dst = dst_pos; 443 for (w = width; w > 0; w -= BLOCKSIZE_W) { 444 WSCONS_blit16((Uint8 *)src, 445 srcRightDelta, 446 srcDownDelta, 447 (Uint8 *)dst, 448 dst_linebytes, 449 min(w, BLOCKSIZE_W), 450 min(height, BLOCKSIZE_H)); 451 src += srcRightDelta * BLOCKSIZE_W; 452 dst += BLOCKSIZE_W; 453 } 454 dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes * BLOCKSIZE_H); 455 src_pos += srcDownDelta * BLOCKSIZE_H; 456 height -= BLOCKSIZE_H; 457 } 458} 459 460static void WSCONS_UpdateRects(_THIS, int numrects, SDL_Rect *rects) 461{ 462 int width = private->SDL_modelist[0]->w; 463 int height = private->SDL_modelist[0]->h; 464 int bytesPerPixel = (private->info.depth + 7) / 8; 465 int i; 466 467 if (!private->shadowFB) { 468 return; 469 } 470 471 if (private->info.depth != 16) { 472 WSCONS_ReportError("Shadow copy only implemented for 16 bpp"); 473 return; 474 } 475 476 for (i = 0; i < numrects; i++) { 477 int x1, y1, x2, y2; 478 int scr_x1, scr_y1, scr_x2, scr_y2; 479 int sha_x1, sha_y1; 480 int shadowRightDelta; /* Address change when moving right in dest */ 481 int shadowDownDelta; /* Address change when moving down in dest */ 482 Uint8 *src_start; 483 Uint8 *dst_start; 484 485 x1 = rects[i].x; 486 y1 = rects[i].y; 487 x2 = x1 + rects[i].w; 488 y2 = y1 + rects[i].h; 489 490 if (x1 < 0) { 491 x1 = 0; 492 } else if (x1 > width) { 493 x1 = width; 494 } 495 if (x2 < 0) { 496 x2 = 0; 497 } else if (x2 > width) { 498 x2 = width; 499 } 500 if (y1 < 0) { 501 y1 = 0; 502 } else if (y1 > height) { 503 y1 = height; 504 } 505 if (y2 < 0) { 506 y2 = 0; 507 } else if (y2 > height) { 508 y2 = height; 509 } 510 if (x2 <= x1 || y2 <= y1) { 511 continue; 512 } 513 514 switch (private->rotate) { 515 case WSCONS_ROTATE_NONE: 516 sha_x1 = scr_x1 = x1; 517 sha_y1 = scr_y1 = y1; 518 scr_x2 = x2; 519 scr_y2 = y2; 520 shadowRightDelta = 1; 521 shadowDownDelta = width; 522 break; 523 case WSCONS_ROTATE_CCW: 524 scr_x1 = y1; 525 scr_y1 = width - x2; 526 scr_x2 = y2; 527 scr_y2 = width - x1; 528 sha_x1 = x2 - 1; 529 sha_y1 = y1; 530 shadowRightDelta = width; 531 shadowDownDelta = -1; 532 break; 533 case WSCONS_ROTATE_UD: 534 scr_x1 = width - x2; 535 scr_y1 = height - y2; 536 scr_x2 = width - x1; 537 scr_y2 = height - y1; 538 sha_x1 = x2 - 1; 539 sha_y1 = y2 - 1; 540 shadowRightDelta = -1; 541 shadowDownDelta = -width; 542 break; 543 case WSCONS_ROTATE_CW: 544 scr_x1 = height - y2; 545 scr_y1 = x1; 546 scr_x2 = height - y1; 547 scr_y2 = x2; 548 sha_x1 = x1; 549 sha_y1 = y2 - 1; 550 shadowRightDelta = -width; 551 shadowDownDelta = 1; 552 break; 553 default: 554 WSCONS_ReportError("Unknown rotation"); 555 return; 556 } 557 558 src_start = private->shadowmem + (sha_y1 * width + sha_x1) * bytesPerPixel; 559 dst_start = private->physmem + scr_y1 * private->physlinebytes + 560 scr_x1 * bytesPerPixel; 561 562 private->blitFunc(src_start, 563 shadowRightDelta, 564 shadowDownDelta, 565 dst_start, 566 private->physlinebytes, 567 scr_x2 - scr_x1, 568 scr_y2 - scr_y1); 569 } 570} 571 572int WSCONS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) 573{ 574 return 0; 575} 576 577/* 578 * Note: If we are terminated, this could be called in the middle of 579 * another SDL video routine -- notably UpdateRects. 580 */ 581void WSCONS_VideoQuit(_THIS) 582{ 583 int mode = WSDISPLAYIO_MODE_EMUL; 584 585 if (private->shadowmem != NULL) { 586 SDL_free(private->shadowmem); 587 private->shadowmem = NULL; 588 } 589 private->fbstart = NULL; 590 if (this->screen != NULL) { 591 this->screen->pixels = NULL; 592 } 593 594 if (private->SDL_modelist[0] != NULL) { 595 SDL_free(private->SDL_modelist[0]); 596 private->SDL_modelist[0] = NULL; 597 } 598 599 if (ioctl(private->fd, WSDISPLAYIO_SMODE, &mode) == -1) { 600 WSCONS_ReportError("ioctl SMODE"); 601 } 602 603 WSCONS_ReleaseKeyboard(this); 604 605 if (private->fd != -1) { 606 close(private->fd); 607 private->fd = -1; 608 } 609} 610