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 | /* SVGAlib based SDL video driver implementation. |
25 | */ |
26 | |
27 | #include <unistd.h> |
28 | #include <sys/stat.h> |
29 | #include <sys/types.h> |
30 | #include <sys/ioctl.h> |
31 | #include <fcntl.h> |
32 | |
33 | #if defined(__LINUX__) |
34 | #include <linux/vt.h> |
35 | #elif defined(__FREEBSD__) |
36 | #include <sys/consio.h> |
37 | #else |
38 | #error You must choose your operating system here |
39 | #endif |
40 | #include <vga.h> |
41 | #include <vgamouse.h> |
42 | #include <vgakeyboard.h> |
43 | |
44 | #include "SDL_video.h" |
45 | #include "SDL_mouse.h" |
46 | #include "../SDL_sysvideo.h" |
47 | #include "../SDL_pixels_c.h" |
48 | #include "../../events/SDL_events_c.h" |
49 | #include "SDL_svgavideo.h" |
50 | #include "SDL_svgaevents_c.h" |
51 | #include "SDL_svgamouse_c.h" |
52 | |
53 | /* Initialization/Query functions */ |
54 | static int SVGA_VideoInit(_THIS, SDL_PixelFormat *vformat); |
55 | static SDL_Rect **SVGA_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); |
56 | static SDL_Surface *SVGA_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); |
57 | static int SVGA_SetColors(_THIS, int firstcolor, int ncolors, |
58 | SDL_Color *colors); |
59 | static void SVGA_VideoQuit(_THIS); |
60 | |
61 | /* Hardware surface functions */ |
62 | static int SVGA_AllocHWSurface(_THIS, SDL_Surface *surface); |
63 | static int SVGA_LockHWSurface(_THIS, SDL_Surface *surface); |
64 | static int SVGA_FlipHWSurface(_THIS, SDL_Surface *surface); |
65 | static void SVGA_UnlockHWSurface(_THIS, SDL_Surface *surface); |
66 | static void SVGA_FreeHWSurface(_THIS, SDL_Surface *surface); |
67 | |
68 | /* SVGAlib driver bootstrap functions */ |
69 | |
70 | static int SVGA_Available(void) |
71 | { |
72 | /* Check to see if we are root and stdin is a virtual console */ |
73 | int console; |
74 | |
75 | /* SVGALib 1.9.x+ doesn't require root (via /dev/svga) */ |
76 | int svgalib2 = -1; |
77 | |
78 | /* See if we are connected to a virtual terminal */ |
79 | console = STDIN_FILENO; |
80 | #if 0 /* This is no longer needed, SVGAlib can switch consoles for us */ |
81 | if ( console >= 0 ) { |
82 | struct stat sb; |
83 | struct vt_mode dummy; |
84 | |
85 | if ( (fstat(console, &sb) < 0) || |
86 | (ioctl(console, VT_GETMODE, &dummy) < 0) ) { |
87 | console = -1; |
88 | } |
89 | } |
90 | #endif /* 0 */ |
91 | |
92 | /* See if SVGAlib 2.0 is available */ |
93 | svgalib2 = open("/dev/svga", O_RDONLY); |
94 | if (svgalib2 != -1) { |
95 | close(svgalib2); |
96 | } |
97 | |
98 | return(((svgalib2 != -1) || (geteuid() == 0)) && (console >= 0)); |
99 | } |
100 | |
101 | static void SVGA_DeleteDevice(SDL_VideoDevice *device) |
102 | { |
103 | SDL_free(device->hidden); |
104 | SDL_free(device); |
105 | } |
106 | |
107 | static SDL_VideoDevice *SVGA_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 ) { |
114 | SDL_memset(device, 0, (sizeof *device)); |
115 | device->hidden = (struct SDL_PrivateVideoData *) |
116 | SDL_malloc((sizeof *device->hidden)); |
117 | } |
118 | if ( (device == NULL) || (device->hidden == NULL) ) { |
119 | SDL_OutOfMemory(); |
120 | if ( device ) { |
121 | SDL_free(device); |
122 | } |
123 | return(0); |
124 | } |
125 | SDL_memset(device->hidden, 0, (sizeof *device->hidden)); |
126 | |
127 | /* Set the function pointers */ |
128 | device->VideoInit = SVGA_VideoInit; |
129 | device->ListModes = SVGA_ListModes; |
130 | device->SetVideoMode = SVGA_SetVideoMode; |
131 | device->SetColors = SVGA_SetColors; |
132 | device->UpdateRects = NULL; |
133 | device->VideoQuit = SVGA_VideoQuit; |
134 | device->AllocHWSurface = SVGA_AllocHWSurface; |
135 | device->CheckHWBlit = NULL; |
136 | device->FillHWRect = NULL; |
137 | device->SetHWColorKey = NULL; |
138 | device->SetHWAlpha = NULL; |
139 | device->LockHWSurface = SVGA_LockHWSurface; |
140 | device->UnlockHWSurface = SVGA_UnlockHWSurface; |
141 | device->FlipHWSurface = SVGA_FlipHWSurface; |
142 | device->FreeHWSurface = SVGA_FreeHWSurface; |
143 | device->SetCaption = NULL; |
144 | device->SetIcon = NULL; |
145 | device->IconifyWindow = NULL; |
146 | device->GrabInput = NULL; |
147 | device->GetWMInfo = NULL; |
148 | device->InitOSKeymap = SVGA_InitOSKeymap; |
149 | device->PumpEvents = SVGA_PumpEvents; |
150 | |
151 | device->free = SVGA_DeleteDevice; |
152 | |
153 | return device; |
154 | } |
155 | |
156 | VideoBootStrap SVGALIB_bootstrap = { |
157 | "svgalib", "SVGAlib", |
158 | SVGA_Available, SVGA_CreateDevice |
159 | }; |
160 | |
161 | static int SVGA_AddMode(_THIS, int mode, int actually_add) |
162 | { |
163 | int i, j; |
164 | vga_modeinfo *modeinfo; |
165 | |
166 | modeinfo = vga_getmodeinfo(mode); |
167 | |
168 | i = modeinfo->bytesperpixel-1; |
169 | if ( i < 0 ) { |
170 | return 0; |
171 | } |
172 | if ( actually_add ) { |
173 | SDL_Rect saved_rect[2]; |
174 | int saved_mode[2]; |
175 | int b; |
176 | |
177 | /* Add the mode, sorted largest to smallest */ |
178 | b = 0; |
179 | j = 0; |
180 | while ( (SDL_modelist[i][j]->w > modeinfo->width) || |
181 | (SDL_modelist[i][j]->h > modeinfo->height) ) { |
182 | ++j; |
183 | } |
184 | /* Skip modes that are already in our list */ |
185 | if ( (SDL_modelist[i][j]->w == modeinfo->width) && |
186 | (SDL_modelist[i][j]->h == modeinfo->height) ) { |
187 | return(0); |
188 | } |
189 | /* Insert the new mode */ |
190 | saved_rect[b] = *SDL_modelist[i][j]; |
191 | saved_mode[b] = SDL_vgamode[i][j]; |
192 | SDL_modelist[i][j]->w = modeinfo->width; |
193 | SDL_modelist[i][j]->h = modeinfo->height; |
194 | SDL_vgamode[i][j] = mode; |
195 | /* Everybody scoot down! */ |
196 | if ( saved_rect[b].w && saved_rect[b].h ) { |
197 | for ( ++j; SDL_modelist[i][j]->w; ++j ) { |
198 | saved_rect[!b] = *SDL_modelist[i][j]; |
199 | saved_mode[!b] = SDL_vgamode[i][j]; |
200 | *SDL_modelist[i][j] = saved_rect[b]; |
201 | SDL_vgamode[i][j] = saved_mode[b]; |
202 | b = !b; |
203 | } |
204 | *SDL_modelist[i][j] = saved_rect[b]; |
205 | SDL_vgamode[i][j] = saved_mode[b]; |
206 | } |
207 | } else { |
208 | ++SDL_nummodes[i]; |
209 | } |
210 | return(1); |
211 | } |
212 | |
213 | static void SVGA_UpdateVideoInfo(_THIS) |
214 | { |
215 | vga_modeinfo *modeinfo; |
216 | |
217 | this->info.wm_available = 0; |
218 | this->info.hw_available = (banked ? 0 : 1); |
219 | modeinfo = vga_getmodeinfo(vga_getcurrentmode()); |
220 | this->info.video_mem = modeinfo->memory; |
221 | /* FIXME: Add hardware accelerated blit information */ |
222 | #ifdef SVGALIB_DEBUG |
223 | printf("Hardware accelerated blit: %savailable\n", modeinfo->haveblit ? "" : "not "); |
224 | #endif |
225 | } |
226 | |
227 | int SVGA_VideoInit(_THIS, SDL_PixelFormat *vformat) |
228 | { |
229 | int keyboard; |
230 | int i, j; |
231 | int mode, total_modes; |
232 | |
233 | /* Initialize all variables that we clean on shutdown */ |
234 | for ( i=0; i<NUM_MODELISTS; ++i ) { |
235 | SDL_nummodes[i] = 0; |
236 | SDL_modelist[i] = NULL; |
237 | SDL_vgamode[i] = NULL; |
238 | } |
239 | |
240 | /* Initialize the library */ |
241 | vga_disabledriverreport(); |
242 | if ( vga_init() < 0 ) { |
243 | SDL_SetError("Unable to initialize SVGAlib"); |
244 | return(-1); |
245 | } |
246 | vga_setmode(TEXT); |
247 | |
248 | /* Enable mouse and keyboard support */ |
249 | vga_setmousesupport(1); |
250 | keyboard = keyboard_init_return_fd(); |
251 | if ( keyboard < 0 ) { |
252 | SDL_SetError("Unable to initialize keyboard"); |
253 | return(-1); |
254 | } |
255 | if ( SVGA_initkeymaps(keyboard) < 0 ) { |
256 | return(-1); |
257 | } |
258 | keyboard_seteventhandler(SVGA_keyboardcallback); |
259 | |
260 | /* Determine the current screen size */ |
261 | this->info.current_w = 0; |
262 | this->info.current_h = 0; |
263 | |
264 | /* Determine the screen depth (use default 8-bit depth) */ |
265 | vformat->BitsPerPixel = 8; |
266 | |
267 | /* Enumerate the available fullscreen modes */ |
268 | total_modes = 0; |
269 | for ( mode=vga_lastmodenumber(); mode; --mode ) { |
270 | if ( vga_hasmode(mode) ) { |
271 | if ( SVGA_AddMode(this, mode, 0) ) { |
272 | ++total_modes; |
273 | } |
274 | } |
275 | } |
276 | if ( SVGA_AddMode(this, G320x200x256, 0) ) ++total_modes; |
277 | if ( total_modes == 0 ) { |
278 | SDL_SetError("No linear video modes available"); |
279 | return(-1); |
280 | } |
281 | for ( i=0; i<NUM_MODELISTS; ++i ) { |
282 | SDL_vgamode[i] = (int *)SDL_malloc(SDL_nummodes[i]*sizeof(int)); |
283 | if ( SDL_vgamode[i] == NULL ) { |
284 | SDL_OutOfMemory(); |
285 | return(-1); |
286 | } |
287 | SDL_modelist[i] = (SDL_Rect **) |
288 | SDL_malloc((SDL_nummodes[i]+1)*sizeof(SDL_Rect *)); |
289 | if ( SDL_modelist[i] == NULL ) { |
290 | SDL_OutOfMemory(); |
291 | return(-1); |
292 | } |
293 | for ( j=0; j<SDL_nummodes[i]; ++j ) { |
294 | SDL_modelist[i][j]=(SDL_Rect *)SDL_malloc(sizeof(SDL_Rect)); |
295 | if ( SDL_modelist[i][j] == NULL ) { |
296 | SDL_OutOfMemory(); |
297 | return(-1); |
298 | } |
299 | SDL_memset(SDL_modelist[i][j], 0, sizeof(SDL_Rect)); |
300 | } |
301 | SDL_modelist[i][j] = NULL; |
302 | } |
303 | for ( mode=vga_lastmodenumber(); mode; --mode ) { |
304 | if ( vga_hasmode(mode) ) { |
305 | SVGA_AddMode(this, mode, 1); |
306 | } |
307 | } |
308 | SVGA_AddMode(this, G320x200x256, 1); |
309 | |
310 | /* Free extra (duplicated) modes */ |
311 | for ( i=0; i<NUM_MODELISTS; ++i ) { |
312 | j = 0; |
313 | while ( SDL_modelist[i][j] && SDL_modelist[i][j]->w ) { |
314 | j++; |
315 | } |
316 | while ( SDL_modelist[i][j] ) { |
317 | SDL_free(SDL_modelist[i][j]); |
318 | SDL_modelist[i][j] = NULL; |
319 | j++; |
320 | } |
321 | } |
322 | |
323 | /* Fill in our hardware acceleration capabilities */ |
324 | SVGA_UpdateVideoInfo(this); |
325 | |
326 | /* We're done! */ |
327 | return(0); |
328 | } |
329 | |
330 | SDL_Rect **SVGA_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) |
331 | { |
332 | return(SDL_modelist[((format->BitsPerPixel+7)/8)-1]); |
333 | } |
334 | |
335 | /* Various screen update functions available */ |
336 | static void SVGA_DirectUpdate(_THIS, int numrects, SDL_Rect *rects); |
337 | static void SVGA_BankedUpdate(_THIS, int numrects, SDL_Rect *rects); |
338 | |
339 | SDL_Surface *SVGA_SetVideoMode(_THIS, SDL_Surface *current, |
340 | int width, int height, int bpp, Uint32 flags) |
341 | { |
342 | int mode; |
343 | int vgamode; |
344 | vga_modeinfo *modeinfo; |
345 | int screenpage_len; |
346 | |
347 | /* Free old pixels if we were in banked mode */ |
348 | if ( banked && current->pixels ) { |
349 | free(current->pixels); |
350 | current->pixels = NULL; |
351 | } |
352 | |
353 | /* Try to set the requested linear video mode */ |
354 | bpp = (bpp+7)/8-1; |
355 | for ( mode=0; SDL_modelist[bpp][mode]; ++mode ) { |
356 | if ( (SDL_modelist[bpp][mode]->w == width) && |
357 | (SDL_modelist[bpp][mode]->h == height) ) { |
358 | break; |
359 | } |
360 | } |
361 | if ( SDL_modelist[bpp][mode] == NULL ) { |
362 | SDL_SetError("Couldn't find requested mode in list"); |
363 | return(NULL); |
364 | } |
365 | vgamode = SDL_vgamode[bpp][mode]; |
366 | vga_setmode(vgamode); |
367 | vga_setpage(0); |
368 | |
369 | if ( (vga_setlinearaddressing() < 0) && (vgamode != G320x200x256) ) { |
370 | banked = 1; |
371 | } else { |
372 | banked = 0; |
373 | } |
374 | |
375 | modeinfo = vga_getmodeinfo(SDL_vgamode[bpp][mode]); |
376 | |
377 | /* Update hardware acceleration info */ |
378 | SVGA_UpdateVideoInfo(this); |
379 | |
380 | /* Allocate the new pixel format for the screen */ |
381 | bpp = (bpp+1)*8; |
382 | if ( (bpp == 16) && (modeinfo->colors == 32768) ) { |
383 | bpp = 15; |
384 | } |
385 | if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) { |
386 | return(NULL); |
387 | } |
388 | |
389 | /* Set up the new mode framebuffer */ |
390 | current->flags = SDL_FULLSCREEN; |
391 | if ( !banked ) { |
392 | current->flags |= SDL_HWSURFACE; |
393 | } |
394 | if ( bpp == 8 ) { |
395 | /* FIXME: What about DirectColor? */ |
396 | current->flags |= SDL_HWPALETTE; |
397 | } |
398 | current->w = width; |
399 | current->h = height; |
400 | current->pitch = modeinfo->linewidth; |
401 | if ( banked ) { |
402 | current->pixels = SDL_malloc(current->h * current->pitch); |
403 | if ( !current->pixels ) { |
404 | SDL_OutOfMemory(); |
405 | return(NULL); |
406 | } |
407 | } else { |
408 | current->pixels = vga_getgraphmem(); |
409 | } |
410 | |
411 | /* set double-buffering */ |
412 | if ( (flags & SDL_DOUBLEBUF) && !banked ) |
413 | { |
414 | /* length of one screen page in bytes */ |
415 | screenpage_len=current->h*modeinfo->linewidth; |
416 | |
417 | /* if start address should be aligned */ |
418 | if ( modeinfo->linewidth_unit ) |
419 | { |
420 | if ( screenpage_len % modeinfo->linewidth_unit ) |
421 | { |
422 | screenpage_len += modeinfo->linewidth_unit - ( screenpage_len % modeinfo->linewidth_unit ); |
423 | } |
424 | } |
425 | |
426 | /* if we heve enough videomemory = ak je dost videopamete */ |
427 | if ( modeinfo->memory > ( screenpage_len * 2 / 1024 ) ) |
428 | { |
429 | current->flags |= SDL_DOUBLEBUF; |
430 | flip_page = 0; |
431 | flip_offset[0] = 0; |
432 | flip_offset[1] = screenpage_len; |
433 | flip_address[0] = vga_getgraphmem(); |
434 | flip_address[1] = flip_address[0]+screenpage_len; |
435 | SVGA_FlipHWSurface(this,current); |
436 | } |
437 | } |
438 | |
439 | /* Set the blit function */ |
440 | if ( banked ) { |
441 | this->UpdateRects = SVGA_BankedUpdate; |
442 | } else { |
443 | this->UpdateRects = SVGA_DirectUpdate; |
444 | } |
445 | |
446 | /* Set up the mouse handler again (buggy SVGAlib 1.40) */ |
447 | mouse_seteventhandler(SVGA_mousecallback); |
448 | |
449 | /* We're done */ |
450 | return(current); |
451 | } |
452 | |
453 | /* We don't actually allow hardware surfaces other than the main one */ |
454 | static int SVGA_AllocHWSurface(_THIS, SDL_Surface *surface) |
455 | { |
456 | return(-1); |
457 | } |
458 | static void SVGA_FreeHWSurface(_THIS, SDL_Surface *surface) |
459 | { |
460 | return; |
461 | } |
462 | |
463 | /* We need to wait for vertical retrace on page flipped displays */ |
464 | static int SVGA_LockHWSurface(_THIS, SDL_Surface *surface) |
465 | { |
466 | /* The waiting is done in SVGA_FlipHWSurface() */ |
467 | return(0); |
468 | } |
469 | static void SVGA_UnlockHWSurface(_THIS, SDL_Surface *surface) |
470 | { |
471 | return; |
472 | } |
473 | |
474 | static int SVGA_FlipHWSurface(_THIS, SDL_Surface *surface) |
475 | { |
476 | if ( !banked ) { |
477 | vga_setdisplaystart(flip_offset[flip_page]); |
478 | flip_page=!flip_page; |
479 | surface->pixels=flip_address[flip_page]; |
480 | vga_waitretrace(); |
481 | } |
482 | return(0); |
483 | } |
484 | |
485 | static void SVGA_DirectUpdate(_THIS, int numrects, SDL_Rect *rects) |
486 | { |
487 | return; |
488 | } |
489 | |
490 | static void SVGA_BankedUpdate(_THIS, int numrects, SDL_Rect *rects) |
491 | { |
492 | int i, j; |
493 | SDL_Rect *rect; |
494 | int page, vp; |
495 | int x, y, w, h; |
496 | unsigned char *src; |
497 | unsigned char *dst; |
498 | int bpp = this->screen->format->BytesPerPixel; |
499 | int pitch = this->screen->pitch; |
500 | |
501 | dst = vga_getgraphmem(); |
502 | for ( i=0; i < numrects; ++i ) { |
503 | rect = &rects[i]; |
504 | x = rect->x; |
505 | y = rect->y; |
506 | w = rect->w * bpp; |
507 | h = rect->h; |
508 | |
509 | vp = y * pitch + x * bpp; |
510 | src = (unsigned char *)this->screen->pixels + vp; |
511 | page = vp >> 16; |
512 | vp &= 0xffff; |
513 | vga_setpage(page); |
514 | for (j = 0; j < h; j++) { |
515 | if (vp + w > 0x10000) { |
516 | if (vp >= 0x10000) { |
517 | page++; |
518 | vga_setpage(page); |
519 | vp &= 0xffff; |
520 | } else { |
521 | SDL_memcpy(dst + vp, src, 0x10000 - vp); |
522 | page++; |
523 | vga_setpage(page); |
524 | SDL_memcpy(dst, src + 0x10000 - vp, |
525 | (vp + w) & 0xffff); |
526 | vp = (vp + pitch) & 0xffff; |
527 | src += pitch; |
528 | continue; |
529 | } |
530 | } |
531 | SDL_memcpy(dst + vp, src, w); |
532 | src += pitch; |
533 | vp += pitch; |
534 | } |
535 | } |
536 | } |
537 | |
538 | int SVGA_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) |
539 | { |
540 | int i; |
541 | |
542 | for(i = 0; i < ncolors; i++) { |
543 | vga_setpalette(firstcolor + i, |
544 | colors[i].r>>2, |
545 | colors[i].g>>2, |
546 | colors[i].b>>2); |
547 | } |
548 | return(1); |
549 | } |
550 | |
551 | /* Note: If we are terminated, this could be called in the middle of |
552 | another SDL video routine -- notably UpdateRects. |
553 | */ |
554 | void SVGA_VideoQuit(_THIS) |
555 | { |
556 | int i, j; |
557 | |
558 | /* Reset the console video mode */ |
559 | if ( this->screen && (this->screen->w && this->screen->h) ) { |
560 | vga_setmode(TEXT); |
561 | } |
562 | keyboard_close(); |
563 | |
564 | /* Free video mode lists */ |
565 | for ( i=0; i<NUM_MODELISTS; ++i ) { |
566 | if ( SDL_modelist[i] != NULL ) { |
567 | for ( j=0; SDL_modelist[i][j]; ++j ) |
568 | SDL_free(SDL_modelist[i][j]); |
569 | SDL_free(SDL_modelist[i]); |
570 | SDL_modelist[i] = NULL; |
571 | } |
572 | if ( SDL_vgamode[i] != NULL ) { |
573 | SDL_free(SDL_vgamode[i]); |
574 | SDL_vgamode[i] = NULL; |
575 | } |
576 | } |
577 | if ( this->screen ) { |
578 | if ( banked && this->screen->pixels ) { |
579 | SDL_free(this->screen->pixels); |
580 | } |
581 | this->screen->pixels = NULL; |
582 | } |
583 | } |
584 | |