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