SDL-1.2.14
[sdl_omap.git] / src / video / fbcon / SDL_fbvideo.c
CommitLineData
e14743d1 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/* Framebuffer console based SDL video driver implementation.
25*/
26
27#include <stdio.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <sys/ioctl.h>
31#include <sys/mman.h>
32
33#ifndef HAVE_GETPAGESIZE
34#include <asm/page.h> /* For definition of PAGE_SIZE */
35#endif
36
37#include <linux/vt.h>
38
39#include "SDL_video.h"
40#include "SDL_mouse.h"
41#include "../SDL_sysvideo.h"
42#include "../SDL_pixels_c.h"
43#include "../../events/SDL_events_c.h"
44#include "SDL_fbvideo.h"
45#include "SDL_fbmouse_c.h"
46#include "SDL_fbevents_c.h"
47#include "SDL_fb3dfx.h"
48#include "SDL_fbmatrox.h"
49#include "SDL_fbriva.h"
50
51/*#define FBCON_DEBUG*/
52
53#if defined(i386) && defined(FB_TYPE_VGA_PLANES)
54#define VGA16_FBCON_SUPPORT
55#include <sys/io.h> /* For ioperm() */
56#ifndef FB_AUX_VGA_PLANES_VGA4
57#define FB_AUX_VGA_PLANES_VGA4 0
58#endif
59/*
60static inline void outb (unsigned char value, unsigned short port)
61{
62 __asm__ __volatile__ ("outb %b0,%w1"::"a" (value), "Nd" (port));
63}
64*/
65#endif /* FB_TYPE_VGA_PLANES */
66
67/* A list of video resolutions that we query for (sorted largest to smallest) */
68static const SDL_Rect checkres[] = {
69 { 0, 0, 1600, 1200 }, /* 16 bpp: 0x11E, or 286 */
70 { 0, 0, 1408, 1056 }, /* 16 bpp: 0x19A, or 410 */
71 { 0, 0, 1280, 1024 }, /* 16 bpp: 0x11A, or 282 */
72 { 0, 0, 1152, 864 }, /* 16 bpp: 0x192, or 402 */
73 { 0, 0, 1024, 768 }, /* 16 bpp: 0x117, or 279 */
74 { 0, 0, 960, 720 }, /* 16 bpp: 0x18A, or 394 */
75 { 0, 0, 800, 600 }, /* 16 bpp: 0x114, or 276 */
76 { 0, 0, 768, 576 }, /* 16 bpp: 0x182, or 386 */
77 { 0, 0, 720, 576 }, /* PAL */
78 { 0, 0, 720, 480 }, /* NTSC */
79 { 0, 0, 640, 480 }, /* 16 bpp: 0x111, or 273 */
80 { 0, 0, 640, 400 }, /* 8 bpp: 0x100, or 256 */
81 { 0, 0, 512, 384 },
82 { 0, 0, 320, 240 },
83 { 0, 0, 320, 200 }
84};
85static const struct {
86 int xres;
87 int yres;
88 int pixclock;
89 int left;
90 int right;
91 int upper;
92 int lower;
93 int hslen;
94 int vslen;
95 int sync;
96 int vmode;
97} vesa_timings[] = {
98#ifdef USE_VESA_TIMINGS /* Only tested on Matrox Millenium I */
99 { 640, 400, 39771, 48, 16, 39, 8, 96, 2, 2, 0 }, /* 70 Hz */
100 { 640, 480, 39683, 48, 16, 33, 10, 96, 2, 0, 0 }, /* 60 Hz */
101 { 768, 576, 26101, 144, 16, 28, 6, 112, 4, 0, 0 }, /* 60 Hz */
102 { 800, 600, 24038, 144, 24, 28, 8, 112, 6, 0, 0 }, /* 60 Hz */
103 { 960, 720, 17686, 144, 24, 28, 8, 112, 4, 0, 0 }, /* 60 Hz */
104 { 1024, 768, 15386, 160, 32, 30, 4, 128, 4, 0, 0 }, /* 60 Hz */
105 { 1152, 864, 12286, 192, 32, 30, 4, 128, 4, 0, 0 }, /* 60 Hz */
106 { 1280, 1024, 9369, 224, 32, 32, 4, 136, 4, 0, 0 }, /* 60 Hz */
107 { 1408, 1056, 8214, 256, 40, 32, 5, 144, 5, 0, 0 }, /* 60 Hz */
108 { 1600, 1200,/*?*/0, 272, 48, 32, 5, 152, 5, 0, 0 }, /* 60 Hz */
109#else
110 /* You can generate these timings from your XF86Config file using
111 the 'modeline2fb' perl script included with the fbset package.
112 These timings were generated for Matrox Millenium I, 15" monitor.
113 */
114 { 320, 200, 79440, 16, 16, 20, 4, 48, 1, 0, 2 }, /* 70 Hz */
115 { 320, 240, 63492, 16, 16, 16, 4, 48, 2, 0, 2 }, /* 72 Hz */
116 { 512, 384, 49603, 48, 16, 16, 1, 64, 3, 0, 0 }, /* 78 Hz */
117 { 640, 400, 31746, 96, 32, 41, 1, 64, 3, 2, 0 }, /* 85 Hz */
118 { 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0, 0 }, /* 75 Hz */
119 { 768, 576, 26101, 144, 16, 28, 6, 112, 4, 0, 0 }, /* 60 Hz */
120 { 800, 600, 20000, 64, 56, 23, 37, 120, 6, 3, 0 }, /* 72 Hz */
121 { 960, 720, 17686, 144, 24, 28, 8, 112, 4, 0, 0 }, /* 60 Hz */
122 { 1024, 768, 13333, 144, 24, 29, 3, 136, 6, 0, 0 }, /* 70 Hz */
123 { 1152, 864, 12286, 192, 32, 30, 4, 128, 4, 0, 0 }, /* 60 Hz */
124 { 1280, 1024, 9369, 224, 32, 32, 4, 136, 4, 0, 0 }, /* 60 Hz */
125 { 1408, 1056, 8214, 256, 40, 32, 5, 144, 5, 0, 0 }, /* 60 Hz */
126 { 1600, 1200,/*?*/0, 272, 48, 32, 5, 152, 5, 0, 0 }, /* 60 Hz */
127#endif
128};
129enum {
130 FBCON_ROTATE_NONE = 0,
131 FBCON_ROTATE_CCW = 90,
132 FBCON_ROTATE_UD = 180,
133 FBCON_ROTATE_CW = 270
134};
135
136#define min(a,b) ((a)<(b)?(a):(b))
137
138/* Initialization/Query functions */
139static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat);
140static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
141static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
142#ifdef VGA16_FBCON_SUPPORT
143static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
144#endif
145static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
146static void FB_VideoQuit(_THIS);
147
148/* Hardware surface functions */
149static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size);
150static void FB_FreeHWSurfaces(_THIS);
151static int FB_AllocHWSurface(_THIS, SDL_Surface *surface);
152static int FB_LockHWSurface(_THIS, SDL_Surface *surface);
153static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface);
154static void FB_FreeHWSurface(_THIS, SDL_Surface *surface);
155static void FB_WaitVBL(_THIS);
156static void FB_WaitIdle(_THIS);
157static int FB_FlipHWSurface(_THIS, SDL_Surface *surface);
158
159/* Internal palette functions */
160static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
161 struct fb_var_screeninfo *vinfo);
162static void FB_RestorePalette(_THIS);
163
164/* Shadow buffer functions */
165static FB_bitBlit FB_blit16;
166static FB_bitBlit FB_blit16blocked;
167
168static int SDL_getpagesize(void)
169{
170#ifdef HAVE_GETPAGESIZE
171 return getpagesize();
172#elif defined(PAGE_SIZE)
173 return PAGE_SIZE;
174#else
175#error Can not determine system page size.
176 return 4096; /* this is what it USED to be in Linux... */
177#endif
178}
179
180
181/* Small wrapper for mmap() so we can play nicely with no-mmu hosts
182 * (non-mmu hosts disallow the MAP_SHARED flag) */
183
184static void *do_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
185{
186 void *ret;
187 ret = mmap(start, length, prot, flags, fd, offset);
188 if ( ret == (char *)-1 && (flags & MAP_SHARED) ) {
189 ret = mmap(start, length, prot,
190 (flags & ~MAP_SHARED) | MAP_PRIVATE, fd, offset);
191 }
192 return ret;
193}
194
195/* FB driver bootstrap functions */
196
197static int FB_Available(void)
198{
199 int console = -1;
200 /* Added check for /fb/0 (devfs) */
201 /* but - use environment variable first... if it fails, still check defaults */
202 int idx = 0;
203 const char *SDL_fbdevs[4] = { NULL, "/dev/fb0", "/dev/fb/0", NULL };
204
205 SDL_fbdevs[0] = SDL_getenv("SDL_FBDEV");
206 if( !SDL_fbdevs[0] )
207 idx++;
208 for( ; SDL_fbdevs[idx]; idx++ )
209 {
210 console = open(SDL_fbdevs[idx], O_RDWR, 0);
211 if ( console >= 0 ) {
212 close(console);
213 break;
214 }
215 }
216 return(console >= 0);
217}
218
219static void FB_DeleteDevice(SDL_VideoDevice *device)
220{
221 SDL_free(device->hidden);
222 SDL_free(device);
223}
224
225static SDL_VideoDevice *FB_CreateDevice(int devindex)
226{
227 SDL_VideoDevice *this;
228
229 /* Initialize all variables that we clean on shutdown */
230 this = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
231 if ( this ) {
232 SDL_memset(this, 0, (sizeof *this));
233 this->hidden = (struct SDL_PrivateVideoData *)
234 SDL_malloc((sizeof *this->hidden));
235 }
236 if ( (this == NULL) || (this->hidden == NULL) ) {
237 SDL_OutOfMemory();
238 if ( this ) {
239 SDL_free(this);
240 }
241 return(0);
242 }
243 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
244 wait_vbl = FB_WaitVBL;
245 wait_idle = FB_WaitIdle;
246 mouse_fd = -1;
247 keyboard_fd = -1;
248
249 /* Set the function pointers */
250 this->VideoInit = FB_VideoInit;
251 this->ListModes = FB_ListModes;
252 this->SetVideoMode = FB_SetVideoMode;
253 this->SetColors = FB_SetColors;
254 this->UpdateRects = NULL;
255 this->VideoQuit = FB_VideoQuit;
256 this->AllocHWSurface = FB_AllocHWSurface;
257 this->CheckHWBlit = NULL;
258 this->FillHWRect = NULL;
259 this->SetHWColorKey = NULL;
260 this->SetHWAlpha = NULL;
261 this->LockHWSurface = FB_LockHWSurface;
262 this->UnlockHWSurface = FB_UnlockHWSurface;
263 this->FlipHWSurface = FB_FlipHWSurface;
264 this->FreeHWSurface = FB_FreeHWSurface;
265 this->SetCaption = NULL;
266 this->SetIcon = NULL;
267 this->IconifyWindow = NULL;
268 this->GrabInput = NULL;
269 this->GetWMInfo = NULL;
270 this->InitOSKeymap = FB_InitOSKeymap;
271 this->PumpEvents = FB_PumpEvents;
272
273 this->free = FB_DeleteDevice;
274
275 return this;
276}
277
278VideoBootStrap FBCON_bootstrap = {
279 "fbcon", "Linux Framebuffer Console",
280 FB_Available, FB_CreateDevice
281};
282
283#define FB_MODES_DB "/etc/fb.modes"
284
285static int read_fbmodes_line(FILE*f, char* line, int length)
286{
287 int blank;
288 char* c;
289 int i;
290
291 blank=0;
292 /* find a relevant line */
293 do
294 {
295 if (!fgets(line,length,f))
296 return 0;
297 c=line;
298 while(((*c=='\t')||(*c==' '))&&(*c!=0))
299 c++;
300
301 if ((*c=='\n')||(*c=='#')||(*c==0))
302 blank=1;
303 else
304 blank=0;
305 }
306 while(blank);
307 /* remove whitespace at the begining of the string */
308 i=0;
309 do
310 {
311 line[i]=c[i];
312 i++;
313 }
314 while(c[i]!=0);
315 return 1;
316}
317
318static int read_fbmodes_mode(FILE *f, struct fb_var_screeninfo *vinfo)
319{
320 char line[1024];
321 char option[256];
322
323 /* Find a "geometry" */
324 do {
325 if (read_fbmodes_line(f, line, sizeof(line))==0)
326 return 0;
327 if (SDL_strncmp(line,"geometry",8)==0)
328 break;
329 }
330 while(1);
331
332 SDL_sscanf(line, "geometry %d %d %d %d %d", &vinfo->xres, &vinfo->yres,
333 &vinfo->xres_virtual, &vinfo->yres_virtual, &vinfo->bits_per_pixel);
334 if (read_fbmodes_line(f, line, sizeof(line))==0)
335 return 0;
336
337 SDL_sscanf(line, "timings %d %d %d %d %d %d %d", &vinfo->pixclock,
338 &vinfo->left_margin, &vinfo->right_margin, &vinfo->upper_margin,
339 &vinfo->lower_margin, &vinfo->hsync_len, &vinfo->vsync_len);
340
341 vinfo->sync=0;
342 vinfo->vmode=FB_VMODE_NONINTERLACED;
343
344 /* Parse misc options */
345 do {
346 if (read_fbmodes_line(f, line, sizeof(line))==0)
347 return 0;
348
349 if (SDL_strncmp(line,"hsync",5)==0) {
350 SDL_sscanf(line,"hsync %s",option);
351 if (SDL_strncmp(option,"high",4)==0)
352 vinfo->sync |= FB_SYNC_HOR_HIGH_ACT;
353 }
354 else if (SDL_strncmp(line,"vsync",5)==0) {
355 SDL_sscanf(line,"vsync %s",option);
356 if (SDL_strncmp(option,"high",4)==0)
357 vinfo->sync |= FB_SYNC_VERT_HIGH_ACT;
358 }
359 else if (SDL_strncmp(line,"csync",5)==0) {
360 SDL_sscanf(line,"csync %s",option);
361 if (SDL_strncmp(option,"high",4)==0)
362 vinfo->sync |= FB_SYNC_COMP_HIGH_ACT;
363 }
364 else if (SDL_strncmp(line,"extsync",5)==0) {
365 SDL_sscanf(line,"extsync %s",option);
366 if (SDL_strncmp(option,"true",4)==0)
367 vinfo->sync |= FB_SYNC_EXT;
368 }
369 else if (SDL_strncmp(line,"laced",5)==0) {
370 SDL_sscanf(line,"laced %s",option);
371 if (SDL_strncmp(option,"true",4)==0)
372 vinfo->vmode |= FB_VMODE_INTERLACED;
373 }
374 else if (SDL_strncmp(line,"double",6)==0) {
375 SDL_sscanf(line,"double %s",option);
376 if (SDL_strncmp(option,"true",4)==0)
377 vinfo->vmode |= FB_VMODE_DOUBLE;
378 }
379 }
380 while(SDL_strncmp(line,"endmode",7)!=0);
381
382 return 1;
383}
384
385static int FB_CheckMode(_THIS, struct fb_var_screeninfo *vinfo,
386 int index, unsigned int *w, unsigned int *h)
387{
388 int mode_okay;
389
390 mode_okay = 0;
391 vinfo->bits_per_pixel = (index+1)*8;
392 vinfo->xres = *w;
393 vinfo->xres_virtual = *w;
394 vinfo->yres = *h;
395 vinfo->yres_virtual = *h;
396 vinfo->activate = FB_ACTIVATE_TEST;
397 if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, vinfo) == 0 ) {
398#ifdef FBCON_DEBUG
399 fprintf(stderr, "Checked mode %dx%d at %d bpp, got mode %dx%d at %d bpp\n", *w, *h, (index+1)*8, vinfo->xres, vinfo->yres, vinfo->bits_per_pixel);
400#endif
401 if ( (((vinfo->bits_per_pixel+7)/8)-1) == index ) {
402 *w = vinfo->xres;
403 *h = vinfo->yres;
404 mode_okay = 1;
405 }
406 }
407 return mode_okay;
408}
409
410static int FB_AddMode(_THIS, int index, unsigned int w, unsigned int h, int check_timings)
411{
412 SDL_Rect *mode;
413 int i;
414 int next_mode;
415
416 /* Check to see if we already have this mode */
417 if ( SDL_nummodes[index] > 0 ) {
418 mode = SDL_modelist[index][SDL_nummodes[index]-1];
419 if ( (mode->w == w) && (mode->h == h) ) {
420#ifdef FBCON_DEBUG
421 fprintf(stderr, "We already have mode %dx%d at %d bytes per pixel\n", w, h, index+1);
422#endif
423 return(0);
424 }
425 }
426
427 /* Only allow a mode if we have a valid timing for it */
428 if ( check_timings ) {
429 int found_timing = 0;
430 for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
431 if ( (w == vesa_timings[i].xres) &&
432 (h == vesa_timings[i].yres) && vesa_timings[i].pixclock ) {
433 found_timing = 1;
434 break;
435 }
436 }
437 if ( !found_timing ) {
438#ifdef FBCON_DEBUG
439 fprintf(stderr, "No valid timing line for mode %dx%d\n", w, h);
440#endif
441 return(0);
442 }
443 }
444
445 /* Set up the new video mode rectangle */
446 mode = (SDL_Rect *)SDL_malloc(sizeof *mode);
447 if ( mode == NULL ) {
448 SDL_OutOfMemory();
449 return(-1);
450 }
451 mode->x = 0;
452 mode->y = 0;
453 mode->w = w;
454 mode->h = h;
455#ifdef FBCON_DEBUG
456 fprintf(stderr, "Adding mode %dx%d at %d bytes per pixel\n", w, h, index+1);
457#endif
458
459 /* Allocate the new list of modes, and fill in the new mode */
460 next_mode = SDL_nummodes[index];
461 SDL_modelist[index] = (SDL_Rect **)
462 SDL_realloc(SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *));
463 if ( SDL_modelist[index] == NULL ) {
464 SDL_OutOfMemory();
465 SDL_nummodes[index] = 0;
466 SDL_free(mode);
467 return(-1);
468 }
469 SDL_modelist[index][next_mode] = mode;
470 SDL_modelist[index][next_mode+1] = NULL;
471 SDL_nummodes[index]++;
472
473 return(0);
474}
475
476static int cmpmodes(const void *va, const void *vb)
477{
478 const SDL_Rect *a = *(const SDL_Rect**)va;
479 const SDL_Rect *b = *(const SDL_Rect**)vb;
480 if ( a->h == b->h )
481 return b->w - a->w;
482 else
483 return b->h - a->h;
484}
485
486static void FB_SortModes(_THIS)
487{
488 int i;
489 for ( i=0; i<NUM_MODELISTS; ++i ) {
490 if ( SDL_nummodes[i] > 0 ) {
491 SDL_qsort(SDL_modelist[i], SDL_nummodes[i], sizeof *SDL_modelist[i], cmpmodes);
492 }
493 }
494}
495
496static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)
497{
498 const int pagesize = SDL_getpagesize();
499 struct fb_fix_screeninfo finfo;
500 struct fb_var_screeninfo vinfo;
501 int i, j;
502 int current_index;
503 unsigned int current_w;
504 unsigned int current_h;
505 const char *SDL_fbdev;
506 const char *rotation;
507 FILE *modesdb;
508
509 /* Initialize the library */
510 SDL_fbdev = SDL_getenv("SDL_FBDEV");
511 if ( SDL_fbdev == NULL ) {
512 SDL_fbdev = "/dev/fb0";
513 }
514 console_fd = open(SDL_fbdev, O_RDWR, 0);
515 if ( console_fd < 0 ) {
516 SDL_SetError("Unable to open %s", SDL_fbdev);
517 return(-1);
518 }
519
520#if !SDL_THREADS_DISABLED
521 /* Create the hardware surface lock mutex */
522 hw_lock = SDL_CreateMutex();
523 if ( hw_lock == NULL ) {
524 SDL_SetError("Unable to create lock mutex");
525 FB_VideoQuit(this);
526 return(-1);
527 }
528#endif
529
530 /* Get the type of video hardware */
531 if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
532 SDL_SetError("Couldn't get console hardware info");
533 FB_VideoQuit(this);
534 return(-1);
535 }
536 switch (finfo.type) {
537 case FB_TYPE_PACKED_PIXELS:
538 /* Supported, no worries.. */
539 break;
540#ifdef VGA16_FBCON_SUPPORT
541 case FB_TYPE_VGA_PLANES:
542 /* VGA16 is supported, but that's it */
543 if ( finfo.type_aux == FB_AUX_VGA_PLANES_VGA4 ) {
544 if ( ioperm(0x3b4, 0x3df - 0x3b4 + 1, 1) < 0 ) {
545 SDL_SetError("No I/O port permissions");
546 FB_VideoQuit(this);
547 return(-1);
548 }
549 this->SetVideoMode = FB_SetVGA16Mode;
550 break;
551 }
552 /* Fall through to unsupported case */
553#endif /* VGA16_FBCON_SUPPORT */
554 default:
555 SDL_SetError("Unsupported console hardware");
556 FB_VideoQuit(this);
557 return(-1);
558 }
559 switch (finfo.visual) {
560 case FB_VISUAL_TRUECOLOR:
561 case FB_VISUAL_PSEUDOCOLOR:
562 case FB_VISUAL_STATIC_PSEUDOCOLOR:
563 case FB_VISUAL_DIRECTCOLOR:
564 break;
565 default:
566 SDL_SetError("Unsupported console hardware");
567 FB_VideoQuit(this);
568 return(-1);
569 }
570
571 /* Check if the user wants to disable hardware acceleration */
572 { const char *fb_accel;
573 fb_accel = SDL_getenv("SDL_FBACCEL");
574 if ( fb_accel ) {
575 finfo.accel = SDL_atoi(fb_accel);
576 }
577 }
578
579 /* Memory map the device, compensating for buggy PPC mmap() */
580 mapped_offset = (((long)finfo.smem_start) -
581 (((long)finfo.smem_start)&~(pagesize-1)));
582 mapped_memlen = finfo.smem_len+mapped_offset;
583 mapped_mem = do_mmap(NULL, mapped_memlen,
584 PROT_READ|PROT_WRITE, MAP_SHARED, console_fd, 0);
585 if ( mapped_mem == (char *)-1 ) {
586 SDL_SetError("Unable to memory map the video hardware");
587 mapped_mem = NULL;
588 FB_VideoQuit(this);
589 return(-1);
590 }
591
592 /* Determine the current screen depth */
593 if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
594 SDL_SetError("Couldn't get console pixel format");
595 FB_VideoQuit(this);
596 return(-1);
597 }
598 vformat->BitsPerPixel = vinfo.bits_per_pixel;
599 if ( vformat->BitsPerPixel < 8 ) {
600 /* Assuming VGA16, we handle this via a shadow framebuffer */
601 vformat->BitsPerPixel = 8;
602 }
603 for ( i=0; i<vinfo.red.length; ++i ) {
604 vformat->Rmask <<= 1;
605 vformat->Rmask |= (0x00000001<<vinfo.red.offset);
606 }
607 for ( i=0; i<vinfo.green.length; ++i ) {
608 vformat->Gmask <<= 1;
609 vformat->Gmask |= (0x00000001<<vinfo.green.offset);
610 }
611 for ( i=0; i<vinfo.blue.length; ++i ) {
612 vformat->Bmask <<= 1;
613 vformat->Bmask |= (0x00000001<<vinfo.blue.offset);
614 }
615 saved_vinfo = vinfo;
616
617 /* Save hardware palette, if needed */
618 FB_SavePalette(this, &finfo, &vinfo);
619
620 /* If the I/O registers are available, memory map them so we
621 can take advantage of any supported hardware acceleration.
622 */
623 vinfo.accel_flags = 0; /* Temporarily reserve registers */
624 ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo);
625 if ( finfo.accel && finfo.mmio_len ) {
626 mapped_iolen = finfo.mmio_len;
627 mapped_io = do_mmap(NULL, mapped_iolen, PROT_READ|PROT_WRITE,
628 MAP_SHARED, console_fd, mapped_memlen);
629 if ( mapped_io == (char *)-1 ) {
630 /* Hmm, failed to memory map I/O registers */
631 mapped_io = NULL;
632 }
633 }
634
635 rotate = FBCON_ROTATE_NONE;
636 rotation = SDL_getenv("SDL_VIDEO_FBCON_ROTATION");
637 if (rotation != NULL) {
638 if (SDL_strlen(rotation) == 0) {
639 shadow_fb = 0;
640 rotate = FBCON_ROTATE_NONE;
641#ifdef FBCON_DEBUG
642 printf("Not rotating, no shadow\n");
643#endif
644 } else if (!SDL_strcmp(rotation, "NONE")) {
645 shadow_fb = 1;
646 rotate = FBCON_ROTATE_NONE;
647#ifdef FBCON_DEBUG
648 printf("Not rotating, but still using shadow\n");
649#endif
650 } else if (!SDL_strcmp(rotation, "CW")) {
651 shadow_fb = 1;
652 rotate = FBCON_ROTATE_CW;
653#ifdef FBCON_DEBUG
654 printf("Rotating screen clockwise\n");
655#endif
656 } else if (!SDL_strcmp(rotation, "CCW")) {
657 shadow_fb = 1;
658 rotate = FBCON_ROTATE_CCW;
659#ifdef FBCON_DEBUG
660 printf("Rotating screen counter clockwise\n");
661#endif
662 } else if (!SDL_strcmp(rotation, "UD")) {
663 shadow_fb = 1;
664 rotate = FBCON_ROTATE_UD;
665#ifdef FBCON_DEBUG
666 printf("Rotating screen upside down\n");
667#endif
668 } else {
669 SDL_SetError("\"%s\" is not a valid value for "
670 "SDL_VIDEO_FBCON_ROTATION", rotation);
671 return(-1);
672 }
673 }
674
675 if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
676 current_w = vinfo.yres;
677 current_h = vinfo.xres;
678 } else {
679 current_w = vinfo.xres;
680 current_h = vinfo.yres;
681 }
682
683 /* Query for the list of available video modes */
684 current_index = ((vinfo.bits_per_pixel+7)/8)-1;
685 modesdb = fopen(FB_MODES_DB, "r");
686 for ( i=0; i<NUM_MODELISTS; ++i ) {
687 SDL_nummodes[i] = 0;
688 SDL_modelist[i] = NULL;
689 }
690 if ( SDL_getenv("SDL_FB_BROKEN_MODES") != NULL ) {
691 FB_AddMode(this, current_index, current_w, current_h, 0);
692 } else if(modesdb) {
693 while ( read_fbmodes_mode(modesdb, &vinfo) ) {
694 for ( i=0; i<NUM_MODELISTS; ++i ) {
695 unsigned int w, h;
696
697 if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
698 w = vinfo.yres;
699 h = vinfo.xres;
700 } else {
701 w = vinfo.xres;
702 h = vinfo.yres;
703 }
704 /* See if we are querying for the current mode */
705 if ( i == current_index ) {
706 if ( (current_w > w) || (current_h > h) ) {
707 /* Only check once */
708 FB_AddMode(this, i, current_w, current_h, 0);
709 current_index = -1;
710 }
711 }
712 if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
713 FB_AddMode(this, i, w, h, 0);
714 }
715 }
716 }
717 fclose(modesdb);
718 FB_SortModes(this);
719 } else {
720 for ( i=0; i<NUM_MODELISTS; ++i ) {
721 for ( j=0; j<(sizeof(checkres)/sizeof(checkres[0])); ++j ) {
722 unsigned int w, h;
723
724 if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
725 w = checkres[j].h;
726 h = checkres[j].w;
727 } else {
728 w = checkres[j].w;
729 h = checkres[j].h;
730 }
731 /* See if we are querying for the current mode */
732 if ( i == current_index ) {
733 if ( (current_w > w) || (current_h > h) ) {
734 /* Only check once */
735 FB_AddMode(this, i, current_w, current_h, 0);
736 current_index = -1;
737 }
738 }
739 if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
740 FB_AddMode(this, i, w, h, 1);
741 }
742 }
743 }
744 }
745
746 this->info.current_w = current_w;
747 this->info.current_h = current_h;
748 this->info.wm_available = 0;
749 this->info.hw_available = !shadow_fb;
750 this->info.video_mem = shadow_fb ? 0 : finfo.smem_len/1024;
751 /* Fill in our hardware acceleration capabilities */
752 if ( mapped_io ) {
753 switch (finfo.accel) {
754 case FB_ACCEL_MATROX_MGA2064W:
755 case FB_ACCEL_MATROX_MGA1064SG:
756 case FB_ACCEL_MATROX_MGA2164W:
757 case FB_ACCEL_MATROX_MGA2164W_AGP:
758 case FB_ACCEL_MATROX_MGAG100:
759 /*case FB_ACCEL_MATROX_MGAG200: G200 acceleration broken! */
760 case FB_ACCEL_MATROX_MGAG400:
761#ifdef FBACCEL_DEBUG
762 printf("Matrox hardware accelerator!\n");
763#endif
764 FB_MatroxAccel(this, finfo.accel);
765 break;
766 case FB_ACCEL_3DFX_BANSHEE:
767#ifdef FBACCEL_DEBUG
768 printf("3DFX hardware accelerator!\n");
769#endif
770 FB_3DfxAccel(this, finfo.accel);
771 break;
772 case FB_ACCEL_NV3:
773 case FB_ACCEL_NV4:
774#ifdef FBACCEL_DEBUG
775 printf("NVidia hardware accelerator!\n");
776#endif
777 FB_RivaAccel(this, finfo.accel);
778 break;
779 default:
780#ifdef FBACCEL_DEBUG
781 printf("Unknown hardware accelerator.\n");
782#endif
783 break;
784 }
785 }
786
787 if (shadow_fb) {
788 shadow_mem = (char *)SDL_malloc(mapped_memlen);
789 if (shadow_mem == NULL) {
790 SDL_SetError("No memory for shadow");
791 return (-1);
792 }
793 }
794
795 /* Enable mouse and keyboard support */
796 if ( FB_OpenKeyboard(this) < 0 ) {
797 FB_VideoQuit(this);
798 return(-1);
799 }
800 if ( FB_OpenMouse(this) < 0 ) {
801 const char *sdl_nomouse;
802
803 sdl_nomouse = SDL_getenv("SDL_NOMOUSE");
804 if ( ! sdl_nomouse ) {
805 SDL_SetError("Unable to open mouse");
806 FB_VideoQuit(this);
807 return(-1);
808 }
809 }
810
811 /* We're done! */
812 return(0);
813}
814
815static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
816{
817 return(SDL_modelist[((format->BitsPerPixel+7)/8)-1]);
818}
819
820/* Various screen update functions available */
821static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects);
822#ifdef VGA16_FBCON_SUPPORT
823static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects);
824#endif
825
826#ifdef FBCON_DEBUG
827static void print_vinfo(struct fb_var_screeninfo *vinfo)
828{
829 fprintf(stderr, "Printing vinfo:\n");
830 fprintf(stderr, "\txres: %d\n", vinfo->xres);
831 fprintf(stderr, "\tyres: %d\n", vinfo->yres);
832 fprintf(stderr, "\txres_virtual: %d\n", vinfo->xres_virtual);
833 fprintf(stderr, "\tyres_virtual: %d\n", vinfo->yres_virtual);
834 fprintf(stderr, "\txoffset: %d\n", vinfo->xoffset);
835 fprintf(stderr, "\tyoffset: %d\n", vinfo->yoffset);
836 fprintf(stderr, "\tbits_per_pixel: %d\n", vinfo->bits_per_pixel);
837 fprintf(stderr, "\tgrayscale: %d\n", vinfo->grayscale);
838 fprintf(stderr, "\tnonstd: %d\n", vinfo->nonstd);
839 fprintf(stderr, "\tactivate: %d\n", vinfo->activate);
840 fprintf(stderr, "\theight: %d\n", vinfo->height);
841 fprintf(stderr, "\twidth: %d\n", vinfo->width);
842 fprintf(stderr, "\taccel_flags: %d\n", vinfo->accel_flags);
843 fprintf(stderr, "\tpixclock: %d\n", vinfo->pixclock);
844 fprintf(stderr, "\tleft_margin: %d\n", vinfo->left_margin);
845 fprintf(stderr, "\tright_margin: %d\n", vinfo->right_margin);
846 fprintf(stderr, "\tupper_margin: %d\n", vinfo->upper_margin);
847 fprintf(stderr, "\tlower_margin: %d\n", vinfo->lower_margin);
848 fprintf(stderr, "\thsync_len: %d\n", vinfo->hsync_len);
849 fprintf(stderr, "\tvsync_len: %d\n", vinfo->vsync_len);
850 fprintf(stderr, "\tsync: %d\n", vinfo->sync);
851 fprintf(stderr, "\tvmode: %d\n", vinfo->vmode);
852 fprintf(stderr, "\tred: %d/%d\n", vinfo->red.length, vinfo->red.offset);
853 fprintf(stderr, "\tgreen: %d/%d\n", vinfo->green.length, vinfo->green.offset);
854 fprintf(stderr, "\tblue: %d/%d\n", vinfo->blue.length, vinfo->blue.offset);
855 fprintf(stderr, "\talpha: %d/%d\n", vinfo->transp.length, vinfo->transp.offset);
856}
857static void print_finfo(struct fb_fix_screeninfo *finfo)
858{
859 fprintf(stderr, "Printing finfo:\n");
860 fprintf(stderr, "\tsmem_start = %p\n", (char *)finfo->smem_start);
861 fprintf(stderr, "\tsmem_len = %d\n", finfo->smem_len);
862 fprintf(stderr, "\ttype = %d\n", finfo->type);
863 fprintf(stderr, "\ttype_aux = %d\n", finfo->type_aux);
864 fprintf(stderr, "\tvisual = %d\n", finfo->visual);
865 fprintf(stderr, "\txpanstep = %d\n", finfo->xpanstep);
866 fprintf(stderr, "\typanstep = %d\n", finfo->ypanstep);
867 fprintf(stderr, "\tywrapstep = %d\n", finfo->ywrapstep);
868 fprintf(stderr, "\tline_length = %d\n", finfo->line_length);
869 fprintf(stderr, "\tmmio_start = %p\n", (char *)finfo->mmio_start);
870 fprintf(stderr, "\tmmio_len = %d\n", finfo->mmio_len);
871 fprintf(stderr, "\taccel = %d\n", finfo->accel);
872}
873#endif
874
875static int choose_fbmodes_mode(struct fb_var_screeninfo *vinfo)
876{
877 int matched;
878 FILE *modesdb;
879 struct fb_var_screeninfo cinfo;
880
881 matched = 0;
882 modesdb = fopen(FB_MODES_DB, "r");
883 if ( modesdb ) {
884 /* Parse the mode definition file */
885 while ( read_fbmodes_mode(modesdb, &cinfo) ) {
886 if ( (vinfo->xres == cinfo.xres && vinfo->yres == cinfo.yres) &&
887 (!matched || (vinfo->bits_per_pixel == cinfo.bits_per_pixel)) ) {
888 vinfo->pixclock = cinfo.pixclock;
889 vinfo->left_margin = cinfo.left_margin;
890 vinfo->right_margin = cinfo.right_margin;
891 vinfo->upper_margin = cinfo.upper_margin;
892 vinfo->lower_margin = cinfo.lower_margin;
893 vinfo->hsync_len = cinfo.hsync_len;
894 vinfo->vsync_len = cinfo.vsync_len;
895 if ( matched ) {
896 break;
897 }
898 matched = 1;
899 }
900 }
901 fclose(modesdb);
902 }
903 return(matched);
904}
905
906static int choose_vesa_mode(struct fb_var_screeninfo *vinfo)
907{
908 int matched;
909 int i;
910
911 /* Check for VESA timings */
912 matched = 0;
913 for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
914 if ( (vinfo->xres == vesa_timings[i].xres) &&
915 (vinfo->yres == vesa_timings[i].yres) ) {
916#ifdef FBCON_DEBUG
917 fprintf(stderr, "Using VESA timings for %dx%d\n",
918 vinfo->xres, vinfo->yres);
919#endif
920 if ( vesa_timings[i].pixclock ) {
921 vinfo->pixclock = vesa_timings[i].pixclock;
922 }
923 vinfo->left_margin = vesa_timings[i].left;
924 vinfo->right_margin = vesa_timings[i].right;
925 vinfo->upper_margin = vesa_timings[i].upper;
926 vinfo->lower_margin = vesa_timings[i].lower;
927 vinfo->hsync_len = vesa_timings[i].hslen;
928 vinfo->vsync_len = vesa_timings[i].vslen;
929 vinfo->sync = vesa_timings[i].sync;
930 vinfo->vmode = vesa_timings[i].vmode;
931 matched = 1;
932 break;
933 }
934 }
935 return(matched);
936}
937
938#ifdef VGA16_FBCON_SUPPORT
939static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current,
940 int width, int height, int bpp, Uint32 flags)
941{
942 struct fb_fix_screeninfo finfo;
943 struct fb_var_screeninfo vinfo;
944
945 /* Set the terminal into graphics mode */
946 if ( FB_EnterGraphicsMode(this) < 0 ) {
947 return(NULL);
948 }
949
950 /* Restore the original palette */
951 FB_RestorePalette(this);
952
953 /* Set the video mode and get the final screen format */
954 if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
955 SDL_SetError("Couldn't get console screen info");
956 return(NULL);
957 }
958 cache_vinfo = vinfo;
959#ifdef FBCON_DEBUG
960 fprintf(stderr, "Printing actual vinfo:\n");
961 print_vinfo(&vinfo);
962#endif
963 if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) {
964 return(NULL);
965 }
966 current->format->palette->ncolors = 16;
967
968 /* Get the fixed information about the console hardware.
969 This is necessary since finfo.line_length changes.
970 */
971 if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
972 SDL_SetError("Couldn't get console hardware info");
973 return(NULL);
974 }
975#ifdef FBCON_DEBUG
976 fprintf(stderr, "Printing actual finfo:\n");
977 print_finfo(&finfo);
978#endif
979
980 /* Save hardware palette, if needed */
981 FB_SavePalette(this, &finfo, &vinfo);
982
983 /* Set up the new mode framebuffer */
984 current->flags = SDL_FULLSCREEN;
985 current->w = vinfo.xres;
986 current->h = vinfo.yres;
987 current->pitch = current->w;
988 current->pixels = SDL_malloc(current->h*current->pitch);
989
990 /* Set the update rectangle function */
991 this->UpdateRects = FB_VGA16Update;
992
993 /* We're done */
994 return(current);
995}
996#endif /* VGA16_FBCON_SUPPORT */
997
998static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
999 int width, int height, int bpp, Uint32 flags)
1000{
1001 struct fb_fix_screeninfo finfo;
1002 struct fb_var_screeninfo vinfo;
1003 int i;
1004 Uint32 Rmask;
1005 Uint32 Gmask;
1006 Uint32 Bmask;
1007 char *surfaces_mem;
1008 int surfaces_len;
1009
1010 /* Set the terminal into graphics mode */
1011 if ( FB_EnterGraphicsMode(this) < 0 ) {
1012 return(NULL);
1013 }
1014
1015 /* Restore the original palette */
1016 FB_RestorePalette(this);
1017
1018 /* Set the video mode and get the final screen format */
1019 if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
1020 SDL_SetError("Couldn't get console screen info");
1021 return(NULL);
1022 }
1023#ifdef FBCON_DEBUG
1024 fprintf(stderr, "Printing original vinfo:\n");
1025 print_vinfo(&vinfo);
1026#endif
1027 /* Do not use double buffering with shadow buffer */
1028 if (shadow_fb) {
1029 flags &= ~SDL_DOUBLEBUF;
1030 }
1031
1032 if ( (vinfo.xres != width) || (vinfo.yres != height) ||
1033 (vinfo.bits_per_pixel != bpp) || (flags & SDL_DOUBLEBUF) ) {
1034 vinfo.activate = FB_ACTIVATE_NOW;
1035 vinfo.accel_flags = 0;
1036 vinfo.bits_per_pixel = bpp;
1037 vinfo.xres = width;
1038 vinfo.xres_virtual = width;
1039 vinfo.yres = height;
1040 if ( flags & SDL_DOUBLEBUF ) {
1041 vinfo.yres_virtual = height*2;
1042 } else {
1043 vinfo.yres_virtual = height;
1044 }
1045 vinfo.xoffset = 0;
1046 vinfo.yoffset = 0;
1047 vinfo.red.length = vinfo.red.offset = 0;
1048 vinfo.green.length = vinfo.green.offset = 0;
1049 vinfo.blue.length = vinfo.blue.offset = 0;
1050 vinfo.transp.length = vinfo.transp.offset = 0;
1051 if ( ! choose_fbmodes_mode(&vinfo) ) {
1052 choose_vesa_mode(&vinfo);
1053 }
1054#ifdef FBCON_DEBUG
1055 fprintf(stderr, "Printing wanted vinfo:\n");
1056 print_vinfo(&vinfo);
1057#endif
1058 if ( !shadow_fb &&
1059 ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
1060 vinfo.yres_virtual = height;
1061 if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
1062 SDL_SetError("Couldn't set console screen info");
1063 return(NULL);
1064 }
1065 }
1066 } else {
1067 int maxheight;
1068
1069 /* Figure out how much video memory is available */
1070 if ( flags & SDL_DOUBLEBUF ) {
1071 maxheight = height*2;
1072 } else {
1073 maxheight = height;
1074 }
1075 if ( vinfo.yres_virtual > maxheight ) {
1076 vinfo.yres_virtual = maxheight;
1077 }
1078 }
1079 cache_vinfo = vinfo;
1080#ifdef FBCON_DEBUG
1081 fprintf(stderr, "Printing actual vinfo:\n");
1082 print_vinfo(&vinfo);
1083#endif
1084 Rmask = 0;
1085 for ( i=0; i<vinfo.red.length; ++i ) {
1086 Rmask <<= 1;
1087 Rmask |= (0x00000001<<vinfo.red.offset);
1088 }
1089 Gmask = 0;
1090 for ( i=0; i<vinfo.green.length; ++i ) {
1091 Gmask <<= 1;
1092 Gmask |= (0x00000001<<vinfo.green.offset);
1093 }
1094 Bmask = 0;
1095 for ( i=0; i<vinfo.blue.length; ++i ) {
1096 Bmask <<= 1;
1097 Bmask |= (0x00000001<<vinfo.blue.offset);
1098 }
1099 if ( ! SDL_ReallocFormat(current, vinfo.bits_per_pixel,
1100 Rmask, Gmask, Bmask, 0) ) {
1101 return(NULL);
1102 }
1103
1104 /* Get the fixed information about the console hardware.
1105 This is necessary since finfo.line_length changes.
1106 */
1107 if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
1108 SDL_SetError("Couldn't get console hardware info");
1109 return(NULL);
1110 }
1111
1112 /* Save hardware palette, if needed */
1113 FB_SavePalette(this, &finfo, &vinfo);
1114
1115 if (shadow_fb) {
1116 if (vinfo.bits_per_pixel == 16) {
1117 blitFunc = (rotate == FBCON_ROTATE_NONE ||
1118 rotate == FBCON_ROTATE_UD) ?
1119 FB_blit16 : FB_blit16blocked;
1120 } else {
1121#ifdef FBCON_DEBUG
1122 fprintf(stderr, "Init vinfo:\n");
1123 print_vinfo(&vinfo);
1124#endif
1125 SDL_SetError("Using software buffer, but no blitter "
1126 "function is available for %d bpp.",
1127 vinfo.bits_per_pixel);
1128 return(NULL);
1129 }
1130 }
1131
1132 /* Set up the new mode framebuffer */
1133 current->flags &= SDL_FULLSCREEN;
1134 if (shadow_fb) {
1135 current->flags |= SDL_SWSURFACE;
1136 } else {
1137 current->flags |= SDL_HWSURFACE;
1138 }
1139 current->w = vinfo.xres;
1140 current->h = vinfo.yres;
1141 if (shadow_fb) {
1142 current->pitch = current->w * ((vinfo.bits_per_pixel + 7) / 8);
1143 current->pixels = shadow_mem;
1144 physlinebytes = finfo.line_length;
1145 } else {
1146 current->pitch = finfo.line_length;
1147 current->pixels = mapped_mem+mapped_offset;
1148 }
1149
1150 /* Set up the information for hardware surfaces */
1151 surfaces_mem = (char *)current->pixels +
1152 vinfo.yres_virtual*current->pitch;
1153 surfaces_len = (shadow_fb) ?
1154 0 : (mapped_memlen-(surfaces_mem-mapped_mem));
1155
1156 FB_FreeHWSurfaces(this);
1157 FB_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
1158
1159 /* Let the application know we have a hardware palette */
1160 switch (finfo.visual) {
1161 case FB_VISUAL_PSEUDOCOLOR:
1162 current->flags |= SDL_HWPALETTE;
1163 break;
1164 default:
1165 break;
1166 }
1167
1168 /* Update for double-buffering, if we can */
1169 if ( flags & SDL_DOUBLEBUF ) {
1170 if ( vinfo.yres_virtual == (height*2) ) {
1171 current->flags |= SDL_DOUBLEBUF;
1172 flip_page = 0;
1173 flip_address[0] = (char *)current->pixels;
1174 flip_address[1] = (char *)current->pixels+
1175 current->h*current->pitch;
1176 this->screen = current;
1177 FB_FlipHWSurface(this, current);
1178 this->screen = NULL;
1179 }
1180 }
1181
1182 /* Set the update rectangle function */
1183 this->UpdateRects = FB_DirectUpdate;
1184
1185 /* We're done */
1186 return(current);
1187}
1188
1189#ifdef FBCON_DEBUG
1190void FB_DumpHWSurfaces(_THIS)
1191{
1192 vidmem_bucket *bucket;
1193
1194 printf("Memory left: %d (%d total)\n", surfaces_memleft, surfaces_memtotal);
1195 printf("\n");
1196 printf(" Base Size\n");
1197 for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
1198 printf("Bucket: %p, %d (%s)\n", bucket->base, bucket->size, bucket->used ? "used" : "free");
1199 if ( bucket->prev ) {
1200 if ( bucket->base != bucket->prev->base+bucket->prev->size ) {
1201 printf("Warning, corrupt bucket list! (prev)\n");
1202 }
1203 } else {
1204 if ( bucket != &surfaces ) {
1205 printf("Warning, corrupt bucket list! (!prev)\n");
1206 }
1207 }
1208 if ( bucket->next ) {
1209 if ( bucket->next->base != bucket->base+bucket->size ) {
1210 printf("Warning, corrupt bucket list! (next)\n");
1211 }
1212 }
1213 }
1214 printf("\n");
1215}
1216#endif
1217
1218static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size)
1219{
1220 vidmem_bucket *bucket;
1221
1222 surfaces_memtotal = size;
1223 surfaces_memleft = size;
1224
1225 if ( surfaces_memleft > 0 ) {
1226 bucket = (vidmem_bucket *)SDL_malloc(sizeof(*bucket));
1227 if ( bucket == NULL ) {
1228 SDL_OutOfMemory();
1229 return(-1);
1230 }
1231 bucket->prev = &surfaces;
1232 bucket->used = 0;
1233 bucket->dirty = 0;
1234 bucket->base = base;
1235 bucket->size = size;
1236 bucket->next = NULL;
1237 } else {
1238 bucket = NULL;
1239 }
1240
1241 surfaces.prev = NULL;
1242 surfaces.used = 1;
1243 surfaces.dirty = 0;
1244 surfaces.base = screen->pixels;
1245 surfaces.size = (unsigned int)((long)base - (long)surfaces.base);
1246 surfaces.next = bucket;
1247 screen->hwdata = (struct private_hwdata *)&surfaces;
1248 return(0);
1249}
1250static void FB_FreeHWSurfaces(_THIS)
1251{
1252 vidmem_bucket *bucket, *freeable;
1253
1254 bucket = surfaces.next;
1255 while ( bucket ) {
1256 freeable = bucket;
1257 bucket = bucket->next;
1258 SDL_free(freeable);
1259 }
1260 surfaces.next = NULL;
1261}
1262
1263static int FB_AllocHWSurface(_THIS, SDL_Surface *surface)
1264{
1265 vidmem_bucket *bucket;
1266 int size;
1267 int extra;
1268
1269/* Temporarily, we only allow surfaces the same width as display.
1270 Some blitters require the pitch between two hardware surfaces
1271 to be the same. Others have interesting alignment restrictions.
1272 Until someone who knows these details looks at the code...
1273*/
1274if ( surface->pitch > SDL_VideoSurface->pitch ) {
1275 SDL_SetError("Surface requested wider than screen");
1276 return(-1);
1277}
1278surface->pitch = SDL_VideoSurface->pitch;
1279 size = surface->h * surface->pitch;
1280#ifdef FBCON_DEBUG
1281 fprintf(stderr, "Allocating bucket of %d bytes\n", size);
1282#endif
1283
1284 /* Quick check for available mem */
1285 if ( size > surfaces_memleft ) {
1286 SDL_SetError("Not enough video memory");
1287 return(-1);
1288 }
1289
1290 /* Search for an empty bucket big enough */
1291 for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
1292 if ( ! bucket->used && (size <= bucket->size) ) {
1293 break;
1294 }
1295 }
1296 if ( bucket == NULL ) {
1297 SDL_SetError("Video memory too fragmented");
1298 return(-1);
1299 }
1300
1301 /* Create a new bucket for left-over memory */
1302 extra = (bucket->size - size);
1303 if ( extra ) {
1304 vidmem_bucket *newbucket;
1305
1306#ifdef FBCON_DEBUG
1307 fprintf(stderr, "Adding new free bucket of %d bytes\n", extra);
1308#endif
1309 newbucket = (vidmem_bucket *)SDL_malloc(sizeof(*newbucket));
1310 if ( newbucket == NULL ) {
1311 SDL_OutOfMemory();
1312 return(-1);
1313 }
1314 newbucket->prev = bucket;
1315 newbucket->used = 0;
1316 newbucket->base = bucket->base+size;
1317 newbucket->size = extra;
1318 newbucket->next = bucket->next;
1319 if ( bucket->next ) {
1320 bucket->next->prev = newbucket;
1321 }
1322 bucket->next = newbucket;
1323 }
1324
1325 /* Set the current bucket values and return it! */
1326 bucket->used = 1;
1327 bucket->size = size;
1328 bucket->dirty = 0;
1329#ifdef FBCON_DEBUG
1330 fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base);
1331#endif
1332 surfaces_memleft -= size;
1333 surface->flags |= SDL_HWSURFACE;
1334 surface->pixels = bucket->base;
1335 surface->hwdata = (struct private_hwdata *)bucket;
1336 return(0);
1337}
1338static void FB_FreeHWSurface(_THIS, SDL_Surface *surface)
1339{
1340 vidmem_bucket *bucket, *freeable;
1341
1342 /* Look for the bucket in the current list */
1343 for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
1344 if ( bucket == (vidmem_bucket *)surface->hwdata ) {
1345 break;
1346 }
1347 }
1348 if ( bucket && bucket->used ) {
1349 /* Add the memory back to the total */
1350#ifdef DGA_DEBUG
1351 printf("Freeing bucket of %d bytes\n", bucket->size);
1352#endif
1353 surfaces_memleft += bucket->size;
1354
1355 /* Can we merge the space with surrounding buckets? */
1356 bucket->used = 0;
1357 if ( bucket->next && ! bucket->next->used ) {
1358#ifdef DGA_DEBUG
1359 printf("Merging with next bucket, for %d total bytes\n", bucket->size+bucket->next->size);
1360#endif
1361 freeable = bucket->next;
1362 bucket->size += bucket->next->size;
1363 bucket->next = bucket->next->next;
1364 if ( bucket->next ) {
1365 bucket->next->prev = bucket;
1366 }
1367 SDL_free(freeable);
1368 }
1369 if ( bucket->prev && ! bucket->prev->used ) {
1370#ifdef DGA_DEBUG
1371 printf("Merging with previous bucket, for %d total bytes\n", bucket->prev->size+bucket->size);
1372#endif
1373 freeable = bucket;
1374 bucket->prev->size += bucket->size;
1375 bucket->prev->next = bucket->next;
1376 if ( bucket->next ) {
1377 bucket->next->prev = bucket->prev;
1378 }
1379 SDL_free(freeable);
1380 }
1381 }
1382 surface->pixels = NULL;
1383 surface->hwdata = NULL;
1384}
1385
1386static int FB_LockHWSurface(_THIS, SDL_Surface *surface)
1387{
1388 if ( switched_away ) {
1389 return -2; /* no hardware access */
1390 }
1391 if ( surface == this->screen ) {
1392 SDL_mutexP(hw_lock);
1393 if ( FB_IsSurfaceBusy(surface) ) {
1394 FB_WaitBusySurfaces(this);
1395 }
1396 } else {
1397 if ( FB_IsSurfaceBusy(surface) ) {
1398 FB_WaitBusySurfaces(this);
1399 }
1400 }
1401 return(0);
1402}
1403static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)
1404{
1405 if ( surface == this->screen ) {
1406 SDL_mutexV(hw_lock);
1407 }
1408}
1409
1410static void FB_WaitVBL(_THIS)
1411{
1412#ifdef FBIOWAITRETRACE /* Heheh, this didn't make it into the main kernel */
1413 ioctl(console_fd, FBIOWAITRETRACE, 0);
1414#endif
1415 return;
1416}
1417
1418static void FB_WaitIdle(_THIS)
1419{
1420 return;
1421}
1422
1423static int FB_FlipHWSurface(_THIS, SDL_Surface *surface)
1424{
1425 if ( switched_away ) {
1426 return -2; /* no hardware access */
1427 }
1428
1429 /* Wait for vertical retrace and then flip display */
1430 cache_vinfo.yoffset = flip_page*surface->h;
1431 if ( FB_IsSurfaceBusy(this->screen) ) {
1432 FB_WaitBusySurfaces(this);
1433 }
1434 wait_vbl(this);
1435 if ( ioctl(console_fd, FBIOPAN_DISPLAY, &cache_vinfo) < 0 ) {
1436 SDL_SetError("ioctl(FBIOPAN_DISPLAY) failed");
1437 return(-1);
1438 }
1439 flip_page = !flip_page;
1440
1441 surface->pixels = flip_address[flip_page];
1442 return(0);
1443}
1444
1445static void FB_blit16(Uint8 *byte_src_pos, int src_right_delta, int src_down_delta,
1446 Uint8 *byte_dst_pos, int dst_linebytes, int width, int height)
1447{
1448 int w;
1449 Uint16 *src_pos = (Uint16 *)byte_src_pos;
1450 Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
1451
1452 while (height) {
1453 Uint16 *src = src_pos;
1454 Uint16 *dst = dst_pos;
1455 for (w = width; w != 0; w--) {
1456 *dst = *src;
1457 src += src_right_delta;
1458 dst++;
1459 }
1460 dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes);
1461 src_pos += src_down_delta;
1462 height--;
1463 }
1464}
1465
1466#define BLOCKSIZE_W 32
1467#define BLOCKSIZE_H 32
1468
1469static void FB_blit16blocked(Uint8 *byte_src_pos, int src_right_delta, int src_down_delta,
1470 Uint8 *byte_dst_pos, int dst_linebytes, int width, int height)
1471{
1472 int w;
1473 Uint16 *src_pos = (Uint16 *)byte_src_pos;
1474 Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
1475
1476 while (height > 0) {
1477 Uint16 *src = src_pos;
1478 Uint16 *dst = dst_pos;
1479 for (w = width; w > 0; w -= BLOCKSIZE_W) {
1480 FB_blit16((Uint8 *)src,
1481 src_right_delta,
1482 src_down_delta,
1483 (Uint8 *)dst,
1484 dst_linebytes,
1485 min(w, BLOCKSIZE_W),
1486 min(height, BLOCKSIZE_H));
1487 src += src_right_delta * BLOCKSIZE_W;
1488 dst += BLOCKSIZE_W;
1489 }
1490 dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes * BLOCKSIZE_H);
1491 src_pos += src_down_delta * BLOCKSIZE_H;
1492 height -= BLOCKSIZE_H;
1493 }
1494}
1495
1496static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
1497{
1498 int width = cache_vinfo.xres;
1499 int height = cache_vinfo.yres;
1500 int bytes_per_pixel = (cache_vinfo.bits_per_pixel + 7) / 8;
1501 int i;
1502
1503 if (!shadow_fb) {
1504 /* The application is already updating the visible video memory */
1505 return;
1506 }
1507
1508 if (cache_vinfo.bits_per_pixel != 16) {
1509 SDL_SetError("Shadow copy only implemented for 16 bpp");
1510 return;
1511 }
1512
1513 for (i = 0; i < numrects; i++) {
1514 int x1, y1, x2, y2;
1515 int scr_x1, scr_y1, scr_x2, scr_y2;
1516 int sha_x1, sha_y1;
1517 int shadow_right_delta; /* Address change when moving right in dest */
1518 int shadow_down_delta; /* Address change when moving down in dest */
1519 char *src_start;
1520 char *dst_start;
1521
1522 x1 = rects[i].x;
1523 y1 = rects[i].y;
1524 x2 = x1 + rects[i].w;
1525 y2 = y1 + rects[i].h;
1526
1527 if (x1 < 0) {
1528 x1 = 0;
1529 } else if (x1 > width) {
1530 x1 = width;
1531 }
1532 if (x2 < 0) {
1533 x2 = 0;
1534 } else if (x2 > width) {
1535 x2 = width;
1536 }
1537 if (y1 < 0) {
1538 y1 = 0;
1539 } else if (y1 > height) {
1540 y1 = height;
1541 }
1542 if (y2 < 0) {
1543 y2 = 0;
1544 } else if (y2 > height) {
1545 y2 = height;
1546 }
1547 if (x2 <= x1 || y2 <= y1) {
1548 continue;
1549 }
1550
1551 switch (rotate) {
1552 case FBCON_ROTATE_NONE:
1553 sha_x1 = scr_x1 = x1;
1554 sha_y1 = scr_y1 = y1;
1555 scr_x2 = x2;
1556 scr_y2 = y2;
1557 shadow_right_delta = 1;
1558 shadow_down_delta = width;
1559 break;
1560 case FBCON_ROTATE_CCW:
1561 scr_x1 = y1;
1562 scr_y1 = width - x2;
1563 scr_x2 = y2;
1564 scr_y2 = width - x1;
1565 sha_x1 = x2 - 1;
1566 sha_y1 = y1;
1567 shadow_right_delta = width;
1568 shadow_down_delta = -1;
1569 break;
1570 case FBCON_ROTATE_UD:
1571 scr_x1 = width - x2;
1572 scr_y1 = height - y2;
1573 scr_x2 = width - x1;
1574 scr_y2 = height - y1;
1575 sha_x1 = x2 - 1;
1576 sha_y1 = y2 - 1;
1577 shadow_right_delta = -1;
1578 shadow_down_delta = -width;
1579 break;
1580 case FBCON_ROTATE_CW:
1581 scr_x1 = height - y2;
1582 scr_y1 = x1;
1583 scr_x2 = height - y1;
1584 scr_y2 = x2;
1585 sha_x1 = x1;
1586 sha_y1 = y2 - 1;
1587 shadow_right_delta = -width;
1588 shadow_down_delta = 1;
1589 break;
1590 default:
1591 SDL_SetError("Unknown rotation");
1592 return;
1593 }
1594
1595 src_start = shadow_mem +
1596 (sha_y1 * width + sha_x1) * bytes_per_pixel;
1597 dst_start = mapped_mem + mapped_offset + scr_y1 * physlinebytes +
1598 scr_x1 * bytes_per_pixel;
1599
1600 blitFunc((Uint8 *) src_start,
1601 shadow_right_delta,
1602 shadow_down_delta,
1603 (Uint8 *) dst_start,
1604 physlinebytes,
1605 scr_x2 - scr_x1,
1606 scr_y2 - scr_y1);
1607 }
1608}
1609
1610#ifdef VGA16_FBCON_SUPPORT
1611/* Code adapted with thanks from the XFree86 VGA16 driver! :) */
1612#define writeGr(index, value) \
1613outb(index, 0x3CE); \
1614outb(value, 0x3CF);
1615#define writeSeq(index, value) \
1616outb(index, 0x3C4); \
1617outb(value, 0x3C5);
1618
1619static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects)
1620{
1621 SDL_Surface *screen;
1622 int width, height, FBPitch, left, i, j, SRCPitch, phase;
1623 register Uint32 m;
1624 Uint8 s1, s2, s3, s4;
1625 Uint32 *src, *srcPtr;
1626 Uint8 *dst, *dstPtr;
1627
1628 if ( switched_away ) {
1629 return; /* no hardware access */
1630 }
1631
1632 screen = this->screen;
1633 FBPitch = screen->w >> 3;
1634 SRCPitch = screen->pitch >> 2;
1635
1636 writeGr(0x03, 0x00);
1637 writeGr(0x05, 0x00);
1638 writeGr(0x01, 0x00);
1639 writeGr(0x08, 0xFF);
1640
1641 while(numrects--) {
1642 left = rects->x & ~7;
1643 width = (rects->w + 7) >> 3;
1644 height = rects->h;
1645 src = (Uint32*)screen->pixels + (rects->y * SRCPitch) + (left >> 2);
1646 dst = (Uint8*)mapped_mem + (rects->y * FBPitch) + (left >> 3);
1647
1648 if((phase = (long)dst & 3L)) {
1649 phase = 4 - phase;
1650 if(phase > width) phase = width;
1651 width -= phase;
1652 }
1653
1654 while(height--) {
1655 writeSeq(0x02, 1 << 0);
1656 dstPtr = dst;
1657 srcPtr = src;
1658 i = width;
1659 j = phase;
1660 while(j--) {
1661 m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
1662 *dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
1663 srcPtr += 2;
1664 }
1665 while(i >= 4) {
1666 m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
1667 s1 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
1668 m = (srcPtr[3] & 0x01010101) | ((srcPtr[2] & 0x01010101) << 4);
1669 s2 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
1670 m = (srcPtr[5] & 0x01010101) | ((srcPtr[4] & 0x01010101) << 4);
1671 s3 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
1672 m = (srcPtr[7] & 0x01010101) | ((srcPtr[6] & 0x01010101) << 4);
1673 s4 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
1674 *((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
1675 srcPtr += 8;
1676 dstPtr += 4;
1677 i -= 4;
1678 }
1679 while(i--) {
1680 m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
1681 *dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
1682 srcPtr += 2;
1683 }
1684
1685 writeSeq(0x02, 1 << 1);
1686 dstPtr = dst;
1687 srcPtr = src;
1688 i = width;
1689 j = phase;
1690 while(j--) {
1691 m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
1692 *dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
1693 srcPtr += 2;
1694 }
1695 while(i >= 4) {
1696 m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
1697 s1 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
1698 m = (srcPtr[3] & 0x02020202) | ((srcPtr[2] & 0x02020202) << 4);
1699 s2 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
1700 m = (srcPtr[5] & 0x02020202) | ((srcPtr[4] & 0x02020202) << 4);
1701 s3 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
1702 m = (srcPtr[7] & 0x02020202) | ((srcPtr[6] & 0x02020202) << 4);
1703 s4 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
1704 *((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
1705 srcPtr += 8;
1706 dstPtr += 4;
1707 i -= 4;
1708 }
1709 while(i--) {
1710 m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
1711 *dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
1712 srcPtr += 2;
1713 }
1714
1715 writeSeq(0x02, 1 << 2);
1716 dstPtr = dst;
1717 srcPtr = src;
1718 i = width;
1719 j = phase;
1720 while(j--) {
1721 m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
1722 *dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
1723 srcPtr += 2;
1724 }
1725 while(i >= 4) {
1726 m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
1727 s1 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
1728 m = (srcPtr[3] & 0x04040404) | ((srcPtr[2] & 0x04040404) << 4);
1729 s2 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
1730 m = (srcPtr[5] & 0x04040404) | ((srcPtr[4] & 0x04040404) << 4);
1731 s3 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
1732 m = (srcPtr[7] & 0x04040404) | ((srcPtr[6] & 0x04040404) << 4);
1733 s4 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
1734 *((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
1735 srcPtr += 8;
1736 dstPtr += 4;
1737 i -= 4;
1738 }
1739 while(i--) {
1740 m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
1741 *dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
1742 srcPtr += 2;
1743 }
1744
1745 writeSeq(0x02, 1 << 3);
1746 dstPtr = dst;
1747 srcPtr = src;
1748 i = width;
1749 j = phase;
1750 while(j--) {
1751 m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
1752 *dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
1753 srcPtr += 2;
1754 }
1755 while(i >= 4) {
1756 m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
1757 s1 = (m >> 27) | (m >> 18) | (m >> 9) | m;
1758 m = (srcPtr[3] & 0x08080808) | ((srcPtr[2] & 0x08080808) << 4);
1759 s2 = (m >> 27) | (m >> 18) | (m >> 9) | m;
1760 m = (srcPtr[5] & 0x08080808) | ((srcPtr[4] & 0x08080808) << 4);
1761 s3 = (m >> 27) | (m >> 18) | (m >> 9) | m;
1762 m = (srcPtr[7] & 0x08080808) | ((srcPtr[6] & 0x08080808) << 4);
1763 s4 = (m >> 27) | (m >> 18) | (m >> 9) | m;
1764 *((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
1765 srcPtr += 8;
1766 dstPtr += 4;
1767 i -= 4;
1768 }
1769 while(i--) {
1770 m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
1771 *dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
1772 srcPtr += 2;
1773 }
1774
1775 dst += FBPitch;
1776 src += SRCPitch;
1777 }
1778 rects++;
1779 }
1780}
1781#endif /* VGA16_FBCON_SUPPORT */
1782
1783void FB_SavePaletteTo(_THIS, int palette_len, __u16 *area)
1784{
1785 struct fb_cmap cmap;
1786
1787 cmap.start = 0;
1788 cmap.len = palette_len;
1789 cmap.red = &area[0*palette_len];
1790 cmap.green = &area[1*palette_len];
1791 cmap.blue = &area[2*palette_len];
1792 cmap.transp = NULL;
1793 ioctl(console_fd, FBIOGETCMAP, &cmap);
1794}
1795
1796void FB_RestorePaletteFrom(_THIS, int palette_len, __u16 *area)
1797{
1798 struct fb_cmap cmap;
1799
1800 cmap.start = 0;
1801 cmap.len = palette_len;
1802 cmap.red = &area[0*palette_len];
1803 cmap.green = &area[1*palette_len];
1804 cmap.blue = &area[2*palette_len];
1805 cmap.transp = NULL;
1806 ioctl(console_fd, FBIOPUTCMAP, &cmap);
1807}
1808
1809static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
1810 struct fb_var_screeninfo *vinfo)
1811{
1812 int i;
1813
1814 /* Save hardware palette, if needed */
1815 if ( finfo->visual == FB_VISUAL_PSEUDOCOLOR ) {
1816 saved_cmaplen = 1<<vinfo->bits_per_pixel;
1817 saved_cmap=(__u16 *)SDL_malloc(3*saved_cmaplen*sizeof(*saved_cmap));
1818 if ( saved_cmap != NULL ) {
1819 FB_SavePaletteTo(this, saved_cmaplen, saved_cmap);
1820 }
1821 }
1822
1823 /* Added support for FB_VISUAL_DIRECTCOLOR.
1824 With this mode pixel information is passed through the palette...
1825 Neat fading and gamma correction effects can be had by simply
1826 fooling around with the palette instead of changing the pixel
1827 values themselves... Very neat!
1828
1829 Adam Meyerowitz 1/19/2000
1830 ameyerow@optonline.com
1831 */
1832 if ( finfo->visual == FB_VISUAL_DIRECTCOLOR ) {
1833 __u16 new_entries[3*256];
1834
1835 /* Save the colormap */
1836 saved_cmaplen = 256;
1837 saved_cmap=(__u16 *)SDL_malloc(3*saved_cmaplen*sizeof(*saved_cmap));
1838 if ( saved_cmap != NULL ) {
1839 FB_SavePaletteTo(this, saved_cmaplen, saved_cmap);
1840 }
1841
1842 /* Allocate new identity colormap */
1843 for ( i=0; i<256; ++i ) {
1844 new_entries[(0*256)+i] =
1845 new_entries[(1*256)+i] =
1846 new_entries[(2*256)+i] = (i<<8)|i;
1847 }
1848 FB_RestorePaletteFrom(this, 256, new_entries);
1849 }
1850}
1851
1852static void FB_RestorePalette(_THIS)
1853{
1854 /* Restore the original palette */
1855 if ( saved_cmap ) {
1856 FB_RestorePaletteFrom(this, saved_cmaplen, saved_cmap);
1857 SDL_free(saved_cmap);
1858 saved_cmap = NULL;
1859 }
1860}
1861
1862static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1863{
1864 int i;
1865 __u16 r[256];
1866 __u16 g[256];
1867 __u16 b[256];
1868 struct fb_cmap cmap;
1869
1870 /* Set up the colormap */
1871 for (i = 0; i < ncolors; i++) {
1872 r[i] = colors[i].r << 8;
1873 g[i] = colors[i].g << 8;
1874 b[i] = colors[i].b << 8;
1875 }
1876 cmap.start = firstcolor;
1877 cmap.len = ncolors;
1878 cmap.red = r;
1879 cmap.green = g;
1880 cmap.blue = b;
1881 cmap.transp = NULL;
1882
1883 if( (ioctl(console_fd, FBIOPUTCMAP, &cmap) < 0) ||
1884 !(this->screen->flags & SDL_HWPALETTE) ) {
1885 colors = this->screen->format->palette->colors;
1886 ncolors = this->screen->format->palette->ncolors;
1887 cmap.start = 0;
1888 cmap.len = ncolors;
1889 SDL_memset(r, 0, sizeof(r));
1890 SDL_memset(g, 0, sizeof(g));
1891 SDL_memset(b, 0, sizeof(b));
1892 if ( ioctl(console_fd, FBIOGETCMAP, &cmap) == 0 ) {
1893 for ( i=ncolors-1; i>=0; --i ) {
1894 colors[i].r = (r[i]>>8);
1895 colors[i].g = (g[i]>>8);
1896 colors[i].b = (b[i]>>8);
1897 }
1898 }
1899 return(0);
1900 }
1901 return(1);
1902}
1903
1904/* Note: If we are terminated, this could be called in the middle of
1905 another SDL video routine -- notably UpdateRects.
1906*/
1907static void FB_VideoQuit(_THIS)
1908{
1909 int i, j;
1910
1911 if ( this->screen ) {
1912 /* Clear screen and tell SDL not to free the pixels */
1913 if ( this->screen->pixels && FB_InGraphicsMode(this) ) {
1914#if defined(__powerpc__) || defined(__ia64__) /* SIGBUS when using SDL_memset() ?? */
1915 Uint8 *rowp = (Uint8 *)this->screen->pixels;
1916 int left = this->screen->pitch*this->screen->h;
1917 while ( left-- ) { *rowp++ = 0; }
1918#else
1919 SDL_memset(this->screen->pixels,0,this->screen->h*this->screen->pitch);
1920#endif
1921 }
1922 /* This test fails when using the VGA16 shadow memory */
1923 if ( ((char *)this->screen->pixels >= mapped_mem) &&
1924 ((char *)this->screen->pixels < (mapped_mem+mapped_memlen)) ) {
1925 this->screen->pixels = NULL;
1926 }
1927 }
1928
1929 /* Clear the lock mutex */
1930 if ( hw_lock ) {
1931 SDL_DestroyMutex(hw_lock);
1932 hw_lock = NULL;
1933 }
1934
1935 /* Clean up defined video modes */
1936 for ( i=0; i<NUM_MODELISTS; ++i ) {
1937 if ( SDL_modelist[i] != NULL ) {
1938 for ( j=0; SDL_modelist[i][j]; ++j ) {
1939 SDL_free(SDL_modelist[i][j]);
1940 }
1941 SDL_free(SDL_modelist[i]);
1942 SDL_modelist[i] = NULL;
1943 }
1944 }
1945
1946 /* Clean up the memory bucket list */
1947 FB_FreeHWSurfaces(this);
1948
1949 /* Close console and input file descriptors */
1950 if ( console_fd > 0 ) {
1951 /* Unmap the video framebuffer and I/O registers */
1952 if ( mapped_mem ) {
1953 munmap(mapped_mem, mapped_memlen);
1954 mapped_mem = NULL;
1955 }
1956 if ( mapped_io ) {
1957 munmap(mapped_io, mapped_iolen);
1958 mapped_io = NULL;
1959 }
1960
1961 /* Restore the original video mode and palette */
1962 if ( FB_InGraphicsMode(this) ) {
1963 FB_RestorePalette(this);
1964 ioctl(console_fd, FBIOPUT_VSCREENINFO, &saved_vinfo);
1965 }
1966
1967 /* We're all done with the framebuffer */
1968 close(console_fd);
1969 console_fd = -1;
1970 }
1971 FB_CloseMouse(this);
1972 FB_CloseKeyboard(this);
1973}