Home | History | Annotate | Download | only in wscons
      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 (at) 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"
     43 enum {
     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 */
     53 static int WSCONS_VideoInit(_THIS, SDL_PixelFormat *vformat);
     54 static SDL_Rect **WSCONS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
     55 static SDL_Surface *WSCONS_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
     56 static int WSCONS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
     57 static void WSCONS_VideoQuit(_THIS);
     58 
     59 /* Hardware surface functions */
     60 static int WSCONS_AllocHWSurface(_THIS, SDL_Surface *surface);
     61 static int WSCONS_LockHWSurface(_THIS, SDL_Surface *surface);
     62 static void WSCONS_UnlockHWSurface(_THIS, SDL_Surface *surface);
     63 static void WSCONS_FreeHWSurface(_THIS, SDL_Surface *surface);
     64 
     65 /* etc. */
     66 static WSCONS_bitBlit WSCONS_blit16;
     67 static WSCONS_bitBlit WSCONS_blit16blocked;
     68 static void WSCONS_UpdateRects(_THIS, int numrects, SDL_Rect *rects);
     69 
     70 void 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 
     87 static int WSCONS_Available(void)
     88 {
     89   return 1;
     90 }
     91 
     92 static void WSCONS_DeleteDevice(SDL_VideoDevice *device)
     93 {
     94   SDL_free(device->hidden);
     95   SDL_free(device);
     96 }
     97 
     98 static 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 
    137 VideoBootStrap 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 
    146 int 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 
    333 SDL_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 
    342 SDL_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 
    382 static int WSCONS_AllocHWSurface(_THIS, SDL_Surface *surface)
    383 {
    384   return -1;
    385 }
    386 static void WSCONS_FreeHWSurface(_THIS, SDL_Surface *surface)
    387 {
    388 }
    389 
    390 static int WSCONS_LockHWSurface(_THIS, SDL_Surface *surface)
    391 {
    392   return 0;
    393 }
    394 
    395 static void WSCONS_UnlockHWSurface(_THIS, SDL_Surface *surface)
    396 {
    397 }
    398 
    399 static 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 
    428 static 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 
    460 static 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 
    572 int 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  */
    581 void 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