Home | History | Annotate | Download | only in picogui
      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     Micah Dowty
     23     micahjd (at) users.sourceforge.net
     24 */
     25 #include "SDL_config.h"
     26 
     27 #include "SDL_video.h"
     28 #include "SDL_mouse.h"
     29 #include "../SDL_sysvideo.h"
     30 #include "../SDL_pixels_c.h"
     31 #include "../../events/SDL_events_c.h"
     32 
     33 #include "SDL_pgvideo.h"
     34 #include "SDL_pgevents_c.h"
     35 
     36 #define PGVID_DRIVER_NAME "picogui"
     37 
     38 /* Initialization/Query functions */
     39 static int PG_VideoInit(_THIS, SDL_PixelFormat *vformat);
     40 static SDL_Rect **PG_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
     41 static SDL_Surface *PG_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
     42 static int PG_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
     43 static void PG_VideoQuit(_THIS);
     44 
     45 /* Hardware surface functions */
     46 static int PG_AllocHWSurface(_THIS, SDL_Surface *surface);
     47 static int PG_LockHWSurface(_THIS, SDL_Surface *surface);
     48 static void PG_UnlockHWSurface(_THIS, SDL_Surface *surface);
     49 static void PG_FreeHWSurface(_THIS, SDL_Surface *surface);
     50 
     51 /* etc. */
     52 static void PG_UpdateRects(_THIS, int numrects, SDL_Rect *rects);
     53 
     54 // The implementation dependent data for the window manager cursor
     55 struct WMcursor {
     56   /* Our cursor is a PicoGUI theme */
     57   pghandle theme;
     58 } ;
     59 
     60 /* WM functions */
     61 void PG_SetCaption(_THIS, const char *title, const char *icon);
     62 WMcursor * PG_CreateWMCursor (_THIS,Uint8 * data, Uint8 * mask,
     63 			      int w, int h, int hot_x, int hot_y);
     64 void PG_FreeWMCursor (_THIS, WMcursor * cursor);
     65 void PG_WarpWMCursor(_THIS, Uint16 x, Uint16 y);
     66 int PG_ShowWMCursor (_THIS, WMcursor * cursor);
     67 
     68 /* PicoGUI driver bootstrap functions */
     69 
     70 static int PG_Available(void)
     71 {
     72         /* FIXME: The current client lib doesn't give a way to see if the picogui
     73 	 *        server is reachable without causing a fatal error if it isn't.
     74 	 *        This should be fixed in cli_c2, but until then assume we can
     75 	 *        connect. Since more common drivers like X11 are probed first anyway,
     76 	 *        this shouldn't be a huge problem.
     77 	 */
     78 	return(1);
     79 }
     80 
     81 static void PG_DeleteDevice(SDL_VideoDevice *device)
     82 {
     83 	SDL_free(device->hidden);
     84 	SDL_free(device);
     85 }
     86 
     87 static SDL_VideoDevice *PG_CreateDevice(int devindex)
     88 {
     89 	SDL_VideoDevice *device;
     90 
     91 	/* Initialize all variables that we clean on shutdown */
     92 	device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
     93 	if ( device ) {
     94 		SDL_memset(device, 0, (sizeof *device));
     95 		device->hidden = (struct SDL_PrivateVideoData *)
     96 				SDL_malloc((sizeof *device->hidden));
     97 	}
     98 	if ( (device == NULL) || (device->hidden == NULL) ) {
     99 		SDL_OutOfMemory();
    100 		if ( device ) {
    101 			SDL_free(device);
    102 		}
    103 		return(0);
    104 	}
    105 	SDL_memset(device->hidden, 0, (sizeof *device->hidden));
    106 
    107 	/* Set the function pointers */
    108 	device->VideoInit = PG_VideoInit;
    109 	device->ListModes = PG_ListModes;
    110 	device->SetVideoMode = PG_SetVideoMode;
    111 	device->CreateYUVOverlay = NULL;
    112 	device->SetColors = PG_SetColors;
    113 	device->UpdateRects = PG_UpdateRects;
    114 	device->VideoQuit = PG_VideoQuit;
    115 	device->AllocHWSurface = PG_AllocHWSurface;
    116 	device->CheckHWBlit = NULL;
    117 	device->FillHWRect = NULL;
    118 	device->SetHWColorKey = NULL;
    119 	device->SetHWAlpha = NULL;
    120 	device->LockHWSurface = PG_LockHWSurface;
    121 	device->UnlockHWSurface = PG_UnlockHWSurface;
    122 	device->FlipHWSurface = NULL;
    123 	device->FreeHWSurface = PG_FreeHWSurface;
    124 	device->SetCaption = PG_SetCaption;
    125 	device->SetIcon = NULL;
    126 	device->IconifyWindow = NULL;
    127 	device->GrabInput = NULL;
    128 
    129 	device->PumpEvents = PG_PumpEvents;
    130 	device->InitOSKeymap = PG_InitOSKeymap;
    131 
    132 	device->ShowWMCursor = PG_ShowWMCursor;
    133 	device->CreateWMCursor = PG_CreateWMCursor;
    134 	device->FreeWMCursor = PG_FreeWMCursor;
    135 	device->WarpWMCursor = PG_WarpWMCursor;
    136 
    137 	device->free = PG_DeleteDevice;
    138 
    139 	return device;
    140 }
    141 
    142 VideoBootStrap PG_bootstrap = {
    143 	PGVID_DRIVER_NAME, "PicoGUI SDL driver",
    144 	PG_Available, PG_CreateDevice
    145 };
    146 
    147 
    148 int PG_VideoInit(_THIS, SDL_PixelFormat *vformat)
    149 {
    150         /* Connect to the PicoGUI server. No way to process command line args yet,
    151 	 * but since this is based on SHM it's not important to be able to specify
    152 	 * a remote PicoGUI server.
    153 	 *
    154 	 * FIXME: Another nitpick about the current client lib is there's no
    155 	 *        clean way to indicate that command line args are not available.
    156 	 */
    157         pgInit(0,(char**)"");
    158 	this->hidden->mi = *pgGetVideoMode();
    159 
    160 	/* Create a picogui application and canvas. We'll populate the canvas later. */
    161 	this->hidden->wApp = pgRegisterApp(PG_APP_NORMAL,"SDL",0);
    162 	this->hidden->wCanvas = pgNewWidget(PG_WIDGET_CANVAS,0,0);
    163 	pgSetWidget(PGDEFAULT,
    164 		    PG_WP_SIDE, PG_S_ALL,
    165 		    0);
    166 
    167 	PG_InitEvents(this);
    168 
    169 	/* Determine the current screen size */
    170 	this->info.current_w = this->hidden->mi.lxres;
    171 	this->info.current_h = this->hidden->mi.lyres;
    172 
    173 	/* Determine the screen depth.
    174 	 * We change this during the SDL_SetVideoMode implementation...
    175 	 * Round up to the nearest Bytes per pixel
    176 	 */
    177 	vformat->BitsPerPixel = this->hidden->mi.bpp;
    178 	vformat->BytesPerPixel = this->hidden->mi.bpp >> 3;
    179 	if (this->hidden->mi.bpp & 7)
    180 	  vformat->BytesPerPixel++;
    181 
    182 	/* We're done! */
    183 	return(0);
    184 }
    185 
    186 SDL_Rect **PG_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
    187 {
    188    	 return (SDL_Rect **) -1;
    189 }
    190 
    191 SDL_Surface *PG_SetVideoMode(_THIS, SDL_Surface *current,
    192 				int width, int height, int bpp, Uint32 flags)
    193 {
    194 	if ( this->hidden->bitmap ) {
    195 	  /* Free old bitmap */
    196 	  if (current->pixels) {
    197 	    shmdt(current->pixels);
    198 	    current->pixels = NULL;
    199 	  }
    200 	  pgDelete(this->hidden->bitmap);
    201 	}
    202 
    203 	/* Allocate the new pixel format for the screen */
    204 	if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) {
    205 		SDL_SetError("Couldn't allocate new pixel format for requested mode");
    206 		return(NULL);
    207 	}
    208 
    209 	/* Create a new picogui bitmap */
    210 	this->hidden->bitmap = pgCreateBitmap(width,height);
    211 	this->hidden->shm = *pgMakeSHMBitmap(this->hidden->bitmap);
    212 	current->pixels = shmat(shmget(this->hidden->shm.shm_key,
    213 				       this->hidden->shm.shm_length,0),NULL,0);
    214 
    215 	/* Reset the canvas, and draw persistent and incremental grops.
    216 	 * Use mapping and offsets to center it.
    217 	 */
    218 
    219 	pgWriteCmd(this->hidden->wCanvas, PGCANVAS_NUKE, 0);
    220 
    221 	/* 0. Set the source position during incremental rendering
    222 	 */
    223 	pgWriteCmd(this->hidden->wCanvas, PGCANVAS_GROP, 5, PG_GROP_SETSRC,0,0,0,0);
    224 	pgWriteCmd(this->hidden->wCanvas, PGCANVAS_GROPFLAGS, 1, PG_GROPF_INCREMENTAL);
    225 
    226 	/* 1. Incremental bitmap rendering
    227 	 */
    228 	pgWriteCmd(this->hidden->wCanvas, PGCANVAS_GROP, 6, PG_GROP_BITMAP,
    229 		   0,0,0,0,this->hidden->bitmap);
    230 	pgWriteCmd(this->hidden->wCanvas, PGCANVAS_GROPFLAGS, 1, PG_GROPF_INCREMENTAL);
    231 
    232 	/* 2. Normal bitmap rendering
    233 	 */
    234 	pgWriteCmd(this->hidden->wCanvas, PGCANVAS_GROP, 6, PG_GROP_BITMAP,
    235 		   0,0,this->hidden->shm.width,this->hidden->shm.height,this->hidden->bitmap);
    236 
    237 	/* Set up the new mode framebuffer */
    238 	current->flags = 0;
    239 	current->w = this->hidden->shm.width;
    240 	current->h = this->hidden->shm.height;
    241 	current->pitch = this->hidden->shm.pitch;
    242 
    243 	/* Set up pixel format */
    244 	current->format->BitsPerPixel = this->hidden->shm.bpp;
    245 	current->format->BytesPerPixel = this->hidden->shm.bpp >> 3;
    246 	if (this->hidden->shm.bpp & 7)
    247 	  current->format->BytesPerPixel++;
    248 	current->format->palette = NULL;
    249 	current->format->Rmask = this->hidden->shm.red_mask;
    250 	current->format->Gmask = this->hidden->shm.green_mask;
    251 	current->format->Bmask = this->hidden->shm.blue_mask;
    252 	current->format->Amask = this->hidden->shm.alpha_mask;
    253 	current->format->Rshift = this->hidden->shm.red_shift;
    254 	current->format->Gshift = this->hidden->shm.green_shift;
    255 	current->format->Bshift = this->hidden->shm.blue_shift;
    256 	current->format->Ashift = this->hidden->shm.alpha_shift;
    257 	current->format->Rloss = 8 - this->hidden->shm.red_length;
    258 	current->format->Gloss = 8 - this->hidden->shm.green_length;
    259 	current->format->Bloss = 8 - this->hidden->shm.blue_length;
    260 	current->format->Aloss = 8 - this->hidden->shm.alpha_length;
    261 
    262 	/* Draw the app */
    263 	pgUpdate();
    264 
    265 	/* We're done */
    266 	return(current);
    267 }
    268 
    269 /* We don't actually allow hardware surfaces other than the main one */
    270 static int PG_AllocHWSurface(_THIS, SDL_Surface *surface)
    271 {
    272 	return(-1);
    273 }
    274 static void PG_FreeHWSurface(_THIS, SDL_Surface *surface)
    275 {
    276 	return;
    277 }
    278 
    279 /* We need to wait for vertical retrace on page flipped displays */
    280 static int PG_LockHWSurface(_THIS, SDL_Surface *surface)
    281 {
    282 	return(0);
    283 }
    284 
    285 static void PG_UnlockHWSurface(_THIS, SDL_Surface *surface)
    286 {
    287 	return;
    288 }
    289 
    290 static void PG_UpdateRects(_THIS, int numrects, SDL_Rect *rects)
    291 {
    292         int i;
    293 
    294 	for (i = 0; i < numrects; i++) {
    295 	        if (rects[i].w <= 0 || rects[i].h <= 0)
    296 		  continue;
    297 
    298 		/* Schedule an incremental update for this rectangle, using
    299 		 * the canvas gropnodes we've loaded beforehand.
    300 		 */
    301 		pgWriteCmd(this->hidden->wCanvas, PGCANVAS_FINDGROP, 1, 0);
    302 		pgWriteCmd(this->hidden->wCanvas, PGCANVAS_MOVEGROP, 4,
    303 			   rects[i].x, rects[i].y,
    304 			   rects[i].w, rects[i].h);
    305 		pgWriteCmd(this->hidden->wCanvas, PGCANVAS_FINDGROP, 1, 1);
    306 		pgWriteCmd(this->hidden->wCanvas, PGCANVAS_MOVEGROP, 4,
    307 			   rects[i].x, rects[i].y,
    308 			   rects[i].w, rects[i].h);
    309 
    310 		/* Go perform the update */
    311 		pgWriteCmd(this->hidden->wCanvas, PGCANVAS_INCREMENTAL, 0);
    312 		pgSubUpdate(this->hidden->wCanvas);
    313 	}
    314 }
    315 
    316 int PG_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
    317 {
    318 	/* do nothing of note. */
    319 	return(1);
    320 }
    321 
    322 /* Note:  If we are terminated, this could be called in the middle of
    323    another SDL video routine -- notably UpdateRects.
    324 */
    325 void PG_VideoQuit(_THIS)
    326 {
    327 	if (this->screen->pixels != NULL)
    328 	{
    329 		shmdt(this->screen->pixels);
    330 		this->screen->pixels = NULL;
    331 		pgDelete(this->hidden->bitmap);
    332 	}
    333 	pgDelete(this->hidden->wCanvas);
    334 	pgDelete(this->hidden->wApp);
    335 }
    336 
    337 void PG_SetCaption(_THIS, const char *title, const char *icon)
    338 {
    339         if (title != NULL)
    340 	  pgReplaceText(this->hidden->wApp, title);
    341 	pgUpdate();
    342 }
    343 
    344 /* FIXME: The cursor stuff isn't implemented yet! */
    345 
    346 WMcursor * PG_CreateWMCursor (_THIS,Uint8 * data, Uint8 * mask,
    347 			      int w, int h, int hot_x, int hot_y)
    348 {
    349         static WMcursor dummy;
    350         return &dummy;
    351 }
    352 
    353 void PG_FreeWMCursor (_THIS, WMcursor * cursor)
    354 {
    355 }
    356 
    357 void PG_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
    358 {
    359 }
    360 
    361 int PG_ShowWMCursor (_THIS, WMcursor * cursor)
    362 {
    363         return 1;
    364 }
    365