1 /* This code is (C) AllegroGL contributors, and double licensed under
2  * the GPL and zlib licenses. See gpl.txt or zlib.txt for details.
3  */
4 /*----------------------------------------------------------------
5  * amesa.c -- Allegro-Mesa interfacing
6  *----------------------------------------------------------------
7  *  This is the interface module for use AllegroGL with Mesa using the AMesa driver.
8  */
9 #include <string.h>
10 
11 #include <GL/gl.h>
12 #include <GL/amesa.h>
13 
14 #include <allegro.h>
15 #include <allegro/internal/aintern.h>
16 
17 #include "alleggl.h"
18 #include "allglint.h"
19 #include "glvtable.h"
20 
21 
22 static void allegro_gl_amesa_exit(BITMAP *bmp);
23 static void __allegro_gl_init_texture_read_format(void);
24 
25 #ifdef GFX_OPENGL_FULLSCREEN
26 static BITMAP *allegro_gl_amesa_fullscreen_init(int w, int h, int vw, int vh, int color_depth);
27 
28 GFX_DRIVER gfx_allegro_gl_fullscreen =
29 {
30    GFX_OPENGL_FULLSCREEN,
31    empty_string,
32    empty_string,
33    "AllegroGL Fullscreen (AMesa)",
34    allegro_gl_amesa_fullscreen_init,
35    allegro_gl_amesa_exit,
36    NULL,
37    NULL,               //_xwin_vsync,
38    NULL,
39    NULL, NULL, NULL,
40    allegro_gl_create_video_bitmap,
41    allegro_gl_destroy_video_bitmap,
42    NULL, NULL,					/* No show/request video bitmaps */
43    NULL, NULL,
44    allegro_gl_set_mouse_sprite,
45    allegro_gl_show_mouse,
46    allegro_gl_hide_mouse,
47    allegro_gl_move_mouse,
48    NULL,
49    NULL, NULL,
50    NULL,                        /* No fetch_mode_list */
51    0, 0,
52    0,
53    0, 0,
54    0,
55    0,
56    FALSE                        /* Windowed mode */
57 };
58 #endif /* GFX_OPENGL_FULLSCREEN */
59 
60 
61 #ifdef GFX_OPENGL_WINDOWED
62 static BITMAP *allegro_gl_amesa_windowed_init(int w, int h, int vw, int vh, int color_depth);
63 
64 GFX_DRIVER gfx_allegro_gl_windowed =
65 {
66    GFX_OPENGL_WINDOWED,
67    empty_string,
68    empty_string,
69    "AllegroGL Windowed (AMesa)",
70    allegro_gl_amesa_windowed_init,
71    allegro_gl_amesa_exit,
72    NULL,
73    NULL,               //_xwin_vsync,
74    NULL,
75    NULL, NULL, NULL,
76    allegro_gl_create_video_bitmap,
77    allegro_gl_destroy_video_bitmap,
78    NULL, NULL,					/* No show/request video bitmaps */
79    NULL, NULL,
80    allegro_gl_set_mouse_sprite,
81    allegro_gl_show_mouse,
82    allegro_gl_hide_mouse,
83    allegro_gl_move_mouse,
84    NULL,
85    NULL, NULL,
86    NULL,                        /* No fetch_mode_list */
87    0, 0,
88    0,
89    0, 0,
90    0,
91    0,
92    TRUE                         /* Windowed mode */
93 };
94 #endif /* GFX_OPENGL_WINDOWED */
95 
96 
97 
98 static int allegro_gl_amesa_create_window (int fullscreen);
99 static BITMAP *allegro_gl_amesa_create_screen_bitmap (GFX_DRIVER *drv, int w, int h, int depth);
100 
101 struct AMESA_DATA {
102     int          fullscreen;
103     AMesaVisual  visual;
104     AMesaBuffer  buffer;
105     AMesaContext context;
106 } AMESA_DATA;
107 
108 static struct AMESA_DATA _amesa;
109 static struct allegro_gl_driver allegro_gl_amesa;
110 
111 static BITMAP* subscreen = NULL;                 /* Sub_bitmap of the virtual screen */
112 static BITMAP* saved_screen = NULL;              /* Saves screen address */
113 
114 GFX_DRIVER *amesa_gfx_driver = NULL;
115 
116 static GFX_VTABLE allegro_gl_generic_vtable;
117 static GFX_VTABLE *old_vtable;
118 
119 
120 
121 /* allegro_gl_amesa_create_screen:
122  *  Creates screen bitmap.
123  */
allegro_gl_amesa_create_screen(int w,int h,int vw,int vh,int depth,int fullscreen)124 static BITMAP *allegro_gl_amesa_create_screen(int w, int h, int vw, int vh, int depth, int fullscreen)
125 {
126 	int _keyboard_was_installed = FALSE;
127 	int _mouse_was_installed = FALSE;
128 	GFX_VTABLE vtable, *pvtable;
129 
130 	pvtable = &vtable;
131 
132 	if (keyboard_driver) {
133 		_keyboard_was_installed = TRUE;
134 		remove_keyboard();
135 		TRACE("* Note * amesa_create_screen: Removing Keyboard...\n");
136 	}
137 
138 	if (mouse_driver) {
139 		_mouse_was_installed = TRUE;
140 		remove_mouse();
141 		TRACE("* Note * amesa_create_screen: Removing Mouse...\n");
142 	}
143 
144 	if ((w == 0) && (h == 0)) {
145 		w = 640;
146 		h = 480;
147 	}
148 
149 	if ((vw > w) || (vh > h)) {
150 		ustrzcpy (allegro_error, ALLEGRO_ERROR_SIZE,
151 		     get_config_text ("OpenGL drivers do not support virtual screens"));
152 		return NULL;
153 	}
154 
155 	allegro_gl_display_info.w = w;
156 	allegro_gl_display_info.h = h;
157 
158    	if (allegro_gl_amesa_create_window(fullscreen)) {
159 		if (fullscreen) {
160 			ustrzcpy (allegro_error, ALLEGRO_ERROR_SIZE,
161 			          get_config_text ("Unable to switch in AMesa fullscreen"));
162 		}
163 		else {
164 			ustrzcpy (allegro_error, ALLEGRO_ERROR_SIZE,
165 			          get_config_text ("Unable to create AMesa window"));
166 		}
167 		return NULL;
168    	}
169 
170 	/* If pixel format is Allegro compatible, set up Allegro correctly. */
171 
172 	if (fullscreen) {
173 #ifdef GFX_OPENGL_FULLSCREEN
174 		allegro_gl_screen = allegro_gl_amesa_create_screen_bitmap (&gfx_allegro_gl_fullscreen, w, h, allegro_gl_display_info.colour_depth);
175 #endif
176     }
177 	else {
178 #ifdef GFX_OPENGL_WINDOWED
179 		allegro_gl_screen = allegro_gl_amesa_create_screen_bitmap (&gfx_allegro_gl_windowed, w, h, allegro_gl_display_info.colour_depth);
180 #endif
181     }
182 
183 	if (!allegro_gl_screen) {
184 		ustrzcpy (allegro_error, ALLEGRO_ERROR_SIZE,
185 		          get_config_text ("Error creating screen bitmap"));
186 		return NULL;
187 	}
188 
189 	__allegro_gl_valid_context = TRUE;
190 	__allegro_gl_driver = &allegro_gl_amesa;
191 	LOCK_DATA(&_amesa, sizeof(AMESA_DATA));
192 	LOCK_DATA(__allegro_gl_driver, sizeof(struct allegro_gl_driver));
193 
194 	/* save the old vtable and create a copy */
195 	old_vtable = allegro_gl_screen->vtable;
196 	memcpy(&allegro_gl_generic_vtable, allegro_gl_screen->vtable,
197 	       sizeof(GFX_VTABLE));
198 	allegro_gl_screen->vtable = &allegro_gl_generic_vtable;
199 
200 	/* The generic driver does not use the glvtable (since it already uses
201 	 * 'pure' Allegro gfx functions. However it needs the AGL-specific
202 	 * draw_glyph method.
203 	 * Hence this hack : we call __allegro_gl__glvtable_update_vtable with
204 	 * 'pvtable' as a parameter since we do not want the regular
205 	 * allegro_gl_screen->vtable to be crushed.
206 	 */
207 	__allegro_gl__glvtable_update_vtable(&pvtable);
208 	allegro_gl_screen->vtable->draw_glyph = pvtable->draw_glyph;
209 	memcpy(&_screen_vtable, allegro_gl_screen->vtable, sizeof(GFX_VTABLE));
210 	allegro_gl_screen->vtable = &_screen_vtable;
211 	__allegro_gl_init_screen_mode();
212 
213 	/* Print out OpenGL version info */
214 	TRACE("\n\nOpenGL Version: %s\nVendor: %s\nRenderer: %s\n",
215 	      (const char*)glGetString(GL_VERSION),
216 	      (const char*)glGetString(GL_VENDOR),
217 	      (const char*)glGetString(GL_RENDERER));
218 
219 	/* Prints out OpenGL extensions info and activates needed extensions */
220 	__allegro_gl_manage_extensions();
221 	__allegro_gl_init_texture_read_format();
222 
223 	if (_keyboard_was_installed) {
224 		install_keyboard();
225 		TRACE("* Note * amesa_create_screen: Installing Keyboard...\n");
226 	}
227 
228 	if (_mouse_was_installed) {
229 		install_mouse();
230 		TRACE("* Note * amesa_create_screen: Installing Mouse...\n");
231 	}
232 
233 	/* XXX <rohannessian> Maybe we should leave this for autodetection? */
234 	gfx_capabilities |= GFX_HW_CURSOR;
235 
236 	allegro_gl_info.is_mesa_driver = TRUE;
237 	_amesa.fullscreen = fullscreen;
238 	return allegro_gl_screen;
239 }
240 
241 
242 
243 #ifdef GFX_OPENGL_WINDOWED
244 /* allegro_gl_amesa_windowed_init:
245  *  Creates screen bitmap for windowed driver.
246  */
allegro_gl_amesa_windowed_init(int w,int h,int vw,int vh,int depth)247 static BITMAP *allegro_gl_amesa_windowed_init(int w, int h, int vw, int vh, int depth)
248 {
249 	return allegro_gl_amesa_create_screen(w, h, vw, vh, depth, FALSE);
250 }
251 #endif
252 
253 
254 
255 #ifdef GFX_OPENGL_FULLSCREEN
256 /* allegro_gl_amesa_fullscreen_init:
257  *  Creates screen bitmap for fullscreen driver.
258  */
allegro_gl_amesa_fullscreen_init(int w,int h,int vw,int vh,int depth)259 static BITMAP *allegro_gl_amesa_fullscreen_init(int w, int h, int vw, int vh, int depth)
260 {
261 	return allegro_gl_amesa_create_screen(w, h, vw, vh, depth, TRUE);
262 }
263 #endif
264 
265 
266 
267 /* allegro_gl_amesa_exit:
268  *  Shuts down the driver (shared between windowed and full-screen)
269  */
allegro_gl_amesa_exit(BITMAP * bmp)270 static void allegro_gl_amesa_exit(BITMAP *bmp)
271 {
272 	/* Restore the screen to its intial value */
273     screen = saved_screen;
274     if (subscreen)
275        destroy_bitmap(subscreen);
276 
277 	amesa_gfx_driver->exit(screen);
278 
279 	AMesaMakeCurrent(_amesa.context, NULL);
280 	AMesaDestroyVisual(_amesa.visual);
281 	AMesaDestroyBuffer(_amesa.buffer);
282 	AMesaDestroyContext(_amesa.context);
283 
284 	__allegro_gl_valid_context = FALSE;
285 }
286 
287 
288 
amesa_choose_gfx_mode(_DRIVER_INFO * driver_info,int * w,int * h,int * colour_depth)289 static void amesa_choose_gfx_mode(_DRIVER_INFO *driver_info, int *w, int *h,
290                                   int *colour_depth)
291 {
292 	GFX_MODE_LIST *mode_list;
293 	GFX_MODE *mode;
294 	int i;
295 
296 	TRACE("* Note * amesa_choose_gfx_mode: GFX driver : %s\n",
297 	      ((GFX_DRIVER*)driver_info->driver)->ascii_name);
298 
299 	/* Try to find a mode which resolution and color depth are higher or
300 	 * equal to those requested
301 	 */
302 	mode_list = get_gfx_mode_list(driver_info->id);
303 
304 	if (mode_list) {
305 		TRACE("* Note * amesa_choose_gfx_mode: %i modes\n",
306 		      mode_list->num_modes);
307 
308 		mode = mode_list->mode;
309 
310 		for (i = 0; i < mode_list->num_modes; i++) {
311 			TRACE("Mode %i : %ix%i %i bpp\n", i, mode->width, mode->height,
312 			       mode->bpp);
313 			if ((mode->width >= *w) && (mode->height >= *h) &&
314 			    (mode->bpp >= *colour_depth)) {
315 				break;
316 			}
317 			if (mode->width) {
318 				mode++;
319 			}
320 		}
321 		if ((mode->width) && (mode->height) && (mode->bpp)) {
322 			allegro_gl_display_info.w = *w = mode->width;
323 			allegro_gl_display_info.h = *h = mode->height;
324 			allegro_gl_display_info.colour_depth = *colour_depth = mode->bpp;
325 		}
326 		TRACE("Best Mode : %ix%i %i bpp\n", *w, *h, *colour_depth);
327 		destroy_gfx_mode_list(mode_list);
328 	}
329 	else {
330 		TRACE("** Warning ** amesa_choose_gfx_mode: Can not list modes...\n"
331 		      "Trying %ix%i %i bpp anyway\n", *w, *h, *colour_depth);
332 	}
333 }
334 
335 
336 
337 /* amesa_set_gfx_mode :
338  * A light version of set_gfx_mode since when this function is reached we are
339  * ALREADY in set_gfx_mode. No need to initialize some parameters.
340  * Moreover we must choose our driver in saved_gfx_drivers and set the real
341  * gfx driver (i.e. the GFX_OPENGL one) the right way.
342  */
amesa_set_gfx_mode(int fullscreen)343 static int amesa_set_gfx_mode(int fullscreen)
344 {
345 	extern void blit_end();
346 	_DRIVER_INFO *driver_list;
347 	int tried = FALSE;
348 	char buf[512], tmp[64];
349 	int c, n;
350 	int card = GFX_AUTODETECT;
351 	int w = allegro_gl_display_info.w;
352 	int h = allegro_gl_display_info.h;
353 	int check_mode = TRUE, require_window = FALSE;
354 
355 	driver_list = saved_gfx_drivers();
356 
357 	if (!fullscreen)
358 		require_window = TRUE;
359 
360 	/* try the drivers that are listed in the config file */
361 	for (n=-2; n<255; n++) {
362 		switch (n) {
363 
364 			case -2:
365 				/* example: gfx_card_640x480x16 = */
366 				usprintf(buf, uconvert_ascii("gfx_card_%dx%dx%d", tmp), w, h,
367 				         _color_depth);
368 				break;
369 
370 			case -1:
371 				/* example: gfx_card_24bpp = */
372 				usprintf(buf, uconvert_ascii("gfx_card_%dbpp", tmp),
373 				         _color_depth);
374 				break;
375 
376 			case 0:
377 				/* example: gfx_card = */
378 				ustrcpy(buf, uconvert_ascii("gfx_card", tmp));
379 				break;
380 
381 			default:
382 				/* example: gfx_card1 = */
383 				usprintf(buf, uconvert_ascii("gfx_card%d", tmp), n);
384 			break;
385 		}
386 		card = get_config_id(uconvert_ascii("graphics", tmp), buf,
387 		                     GFX_AUTODETECT);
388 
389 		if (card != GFX_AUTODETECT) {
390 
391 			for (c=0; driver_list[c].driver; c++) {
392 				if (driver_list[c].id == card) {
393 					amesa_gfx_driver = driver_list[c].driver;
394 
395 					if (check_mode) {
396 						if ( ((require_window) && (!amesa_gfx_driver->windowed))
397 						 || ((!require_window)
398 						  && (amesa_gfx_driver->windowed))) {
399 
400 							amesa_gfx_driver = NULL;
401 							continue;
402 						}
403 					}
404 					break;
405 				}
406 			}
407 
408 			if (amesa_gfx_driver) {
409 				tried = TRUE;
410 				amesa_gfx_driver->name = amesa_gfx_driver->desc
411 				                = get_config_text(amesa_gfx_driver->ascii_name);
412 
413 				amesa_choose_gfx_mode(&driver_list[c], &w, &h, &_color_depth);
414 
415 				screen = amesa_gfx_driver->init(w, h, 0, 0, _color_depth);
416 				if (screen) {
417 					break;
418 				}
419 				else {
420 					amesa_gfx_driver = NULL;
421 				}
422 			}
423 		}
424 		else {
425 			if (n > 1) {
426 				break;
427 			}
428 		}
429 	}
430 
431 	if (!tried) {
432 		for (c=0; driver_list[c].driver; c++) {
433 
434 			if (driver_list[c].autodetect) {
435 
436 				amesa_gfx_driver = driver_list[c].driver;
437 				if (check_mode) {
438 					if (((require_window) && (!amesa_gfx_driver->windowed)) ||
439 					   ((!require_window) && (amesa_gfx_driver->windowed)))
440 						continue;
441 				}
442 
443 				amesa_gfx_driver->name = amesa_gfx_driver->desc
444 				                = get_config_text(amesa_gfx_driver->ascii_name);
445 
446 				amesa_choose_gfx_mode(&driver_list[c], &w, &h, &_color_depth);
447 
448 				screen = amesa_gfx_driver->init(w, h, 0, 0, _color_depth);
449 
450 				if (screen) {
451 					break;
452 				}
453 			}
454 		}
455 	}
456 
457 	if (!screen) {
458 		amesa_gfx_driver = NULL;
459 		return -1;
460 	}
461 
462 	LOCK_DATA(amesa_gfx_driver, sizeof(GFX_DRIVER));
463 	_register_switch_bitmap(screen, NULL);
464 
465 	return 0;
466 }
467 
468 
469 
470 /* create_window:
471  *  Based on Bernhard Tschirren AMesa GLUT code.
472  */
allegro_gl_amesa_create_window(int fullscreen)473 static int allegro_gl_amesa_create_window (int fullscreen)
474 {
475 	if (!allegro_gl_display_info.colour_depth)
476 		allegro_gl_display_info.colour_depth = _color_depth;
477 
478 	set_color_depth(allegro_gl_display_info.colour_depth);
479 
480 	if (amesa_set_gfx_mode(fullscreen)) {
481 		TRACE("** ERROR ** amesa_create_window: Unable to set a gfx mode!\n");
482 		return 1;
483 	}
484 
485 	_amesa.visual  = AMesaCreateVisual(allegro_gl_display_info.doublebuffered,
486 					allegro_gl_display_info.colour_depth,
487 					GL_TRUE,				/* RGBA Mode */
488 					allegro_gl_display_info.depth_size,
489 					allegro_gl_display_info.stencil_size,
490 					allegro_gl_display_info.accum_size.rgba.r,
491 					allegro_gl_display_info.accum_size.rgba.g,
492 					allegro_gl_display_info.accum_size.rgba.b,
493 					allegro_gl_display_info.accum_size.rgba.a
494 					);
495 	if (!_amesa.visual) {
496 		TRACE("** ERROR ** amesa_create_window: Unable to create AMesa "
497 		      "Visual\n");
498 		return 1;
499 	}
500 
501 	_amesa.context = AMesaCreateContext(_amesa.visual, NULL);
502 	if (!_amesa.context) {
503 		TRACE("** ERROR ** amesa_create_window: Unable to create AMesa "
504 		      "Context\n");
505 		AMesaDestroyVisual(_amesa.visual);
506 		return 1;
507 	}
508 
509 	if ((screen->w != allegro_gl_display_info.w)
510 	 || (screen->h != allegro_gl_display_info.h)) {
511 
512 		subscreen = create_sub_bitmap(screen, 0, 0,
513 		                  allegro_gl_display_info.w, allegro_gl_display_info.h);
514 
515 		_amesa.buffer = AMesaCreateBuffer(_amesa.visual, subscreen);
516 
517 		TRACE("** Note ** amesa_create_window: Screen : %ix%i %i bpp\n",
518 		      ubscreen->w, subscreen->h, bitmap_color_depth(subscreen));
519 	}
520 	else {
521 		_amesa.buffer = AMesaCreateBuffer(_amesa.visual, screen);
522 	}
523 
524 	if (!_amesa.buffer) {
525 		AMesaDestroyContext(_amesa.context);
526 		AMesaDestroyVisual(_amesa.visual);
527 		TRACE("** ERROR ** amesa_create_window: Unable to create AMesa "
528 		      "Buffer\n");
529 		return 1;
530 	}
531 
532 	if (!AMesaMakeCurrent(_amesa.context, _amesa.buffer)) {
533 		AMesaDestroyContext(_amesa.context);
534 		AMesaDestroyVisual(_amesa.visual);
535 		AMesaDestroyBuffer(_amesa.buffer);
536 		TRACE("** ERROR ** amesa_create_window: Unable to make context "
537 		      "current\n");
538 		return 1;
539 	}
540 
541 	saved_screen = screen;
542 	return 0;
543 }
544 
545 
546 
allegro_gl_amesa_create_screen_bitmap(GFX_DRIVER * drv,int w,int h,int depth)547 static BITMAP *allegro_gl_amesa_create_screen_bitmap (GFX_DRIVER *drv,
548                                                       int w, int h, int depth)
549 {
550 	drv->w = w;
551 	drv->h = h;
552 	drv->linear        = amesa_gfx_driver->linear;
553 	drv->bank_size     = amesa_gfx_driver->bank_size;
554 	drv->bank_gran     = amesa_gfx_driver->bank_gran;
555 	drv->vid_mem       = amesa_gfx_driver->vid_mem;
556 	drv->vid_phys_base = amesa_gfx_driver->vid_phys_base;
557 
558 	return AMesaGetColorBuffer(_amesa.buffer, AMESA_ACTIVE);
559 }
560 
561 
562 
__allegro_gl_init_texture_read_format(void)563 static void __allegro_gl_init_texture_read_format(void)
564 {
565 	/* 8 bpp (true color mode) */
566 	__allegro_gl_texture_read_format[0] = GL_UNSIGNED_BYTE_3_3_2;
567 
568 	/* 15 bpp */
569 	if (_rgb_r_shift_15 > _rgb_b_shift_15) {
570 		__allegro_gl_texture_read_format[1] = GL_UNSIGNED_SHORT_5_5_5_1;
571 		if (_rgb_r_shift_15 == 10) {
572 			__allegro_gl_texture_components[1] = GL_BGRA;
573 		}
574 	}
575 	else {
576 		__allegro_gl_texture_read_format[1] = GL_UNSIGNED_SHORT_1_5_5_5_REV;
577 	}
578 
579 	/* 16 bpp */
580 	if (_rgb_r_shift_16 > _rgb_b_shift_16) {
581 		__allegro_gl_texture_read_format[2] = GL_UNSIGNED_SHORT_5_6_5;
582 	}
583 	else {
584 		__allegro_gl_texture_read_format[2] = GL_UNSIGNED_SHORT_5_6_5_REV;
585 	}
586 
587 	/* 24 bpp */
588 	__allegro_gl_texture_read_format[3] = GL_UNSIGNED_BYTE;
589 
590 	/* 32 bpp */
591 	if (_rgb_r_shift_32 > _rgb_b_shift_32) {
592 		__allegro_gl_texture_read_format[4] = GL_UNSIGNED_INT_8_8_8_8_REV;
593 		if (_rgb_r_shift_32 == 16) {
594 			__allegro_gl_texture_components[4] = GL_BGRA;
595 		}
596 	}
597 	else {
598 		__allegro_gl_texture_read_format[4] = GL_UNSIGNED_BYTE;
599 	}
600 }
601 
602 
603 
604 /******************************/
605 /* AllegroGL driver functions */
606 /******************************/
607 
608 /* flip:
609  *  Does a page flip / double buffer copy / whatever it really is.
610  */
amesa_flip(void)611 static void amesa_flip (void)
612 {
613 	AMesaSwapBuffers (_amesa.buffer);
614 }
615 
616 
617 
618 /* gl_on, gl_off:
619  *  Switches to/from GL mode.
620  */
amesa_gl_on(void)621 static void amesa_gl_on (void)
622 {
623 }
624 
625 
626 
amesa_gl_off(void)627 static void amesa_gl_off (void)
628 {
629 }
630 
631 
632 
633 /*****************/
634 /* Driver struct */
635 /*****************/
636 
637 static struct allegro_gl_driver allegro_gl_amesa = {
638 	amesa_flip,
639 	amesa_gl_on,
640 	amesa_gl_off
641 };
642 
643