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