Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

vid_so.c (11301B)


      1 // Main windowed and fullscreen graphics interface module. This module
      2 // is used for both the software and OpenGL rendering versions of the
      3 // Quake refresh engine.
      4 
      5 #define SO_FILE "/etc/quake2.conf"
      6 
      7 #include <assert.h>
      8 #include <dlfcn.h> // ELF dl loader
      9 #include <sys/stat.h>
     10 #include <unistd.h>
     11 #include <errno.h>
     12 
     13 #include "../client/client.h"
     14 
     15 #include "../linux/rw_linux.h"
     16 
     17 // Structure containing functions exported from refresh DLL
     18 refexport_t	re;
     19 
     20 // Console variables that we need to access from this module
     21 cvar_t		*vid_gamma;
     22 cvar_t		*vid_ref;			// Name of Refresh DLL loaded
     23 cvar_t		*vid_xpos;			// X coordinate of window position
     24 cvar_t		*vid_ypos;			// Y coordinate of window position
     25 cvar_t		*vid_fullscreen;
     26 
     27 // Global variables used internally by this module
     28 viddef_t	viddef;				// global video state; used by other modules
     29 void		*reflib_library;		// Handle to refresh DLL 
     30 qboolean	reflib_active = 0;
     31 
     32 #define VID_NUM_MODES ( sizeof( vid_modes ) / sizeof( vid_modes[0] ) )
     33 
     34 /** KEYBOARD **************************************************************/
     35 
     36 void Do_Key_Event(int key, qboolean down);
     37 
     38 void (*KBD_Update_fp)(void);
     39 void (*KBD_Init_fp)(Key_Event_fp_t fp);
     40 void (*KBD_Close_fp)(void);
     41 
     42 /** MOUSE *****************************************************************/
     43 
     44 in_state_t in_state;
     45 
     46 void (*RW_IN_Init_fp)(in_state_t *in_state_p);
     47 void (*RW_IN_Shutdown_fp)(void);
     48 void (*RW_IN_Activate_fp)(qboolean active);
     49 void (*RW_IN_Commands_fp)(void);
     50 void (*RW_IN_Move_fp)(usercmd_t *cmd);
     51 void (*RW_IN_Frame_fp)(void);
     52 
     53 void Real_IN_Init (void);
     54 
     55 /*
     56 ==========================================================================
     57 
     58 DLL GLUE
     59 
     60 ==========================================================================
     61 */
     62 
     63 #define	MAXPRINTMSG	4096
     64 void VID_Printf (int print_level, char *fmt, ...)
     65 {
     66 	va_list		argptr;
     67 	char		msg[MAXPRINTMSG];
     68 	static qboolean	inupdate;
     69 	
     70 	va_start (argptr,fmt);
     71 	vsprintf (msg,fmt,argptr);
     72 	va_end (argptr);
     73 
     74 	if (print_level == PRINT_ALL)
     75 		Com_Printf ("%s", msg);
     76 	else
     77 		Com_DPrintf ("%s", msg);
     78 }
     79 
     80 void VID_Error (int err_level, char *fmt, ...)
     81 {
     82 	va_list		argptr;
     83 	char		msg[MAXPRINTMSG];
     84 	static qboolean	inupdate;
     85 	
     86 	va_start (argptr,fmt);
     87 	vsprintf (msg,fmt,argptr);
     88 	va_end (argptr);
     89 
     90 	Com_Error (err_level,"%s", msg);
     91 }
     92 
     93 //==========================================================================
     94 
     95 /*
     96 ============
     97 VID_Restart_f
     98 
     99 Console command to re-start the video mode and refresh DLL. We do this
    100 simply by setting the modified flag for the vid_ref variable, which will
    101 cause the entire video mode and refresh DLL to be reset on the next frame.
    102 ============
    103 */
    104 void VID_Restart_f (void)
    105 {
    106 	vid_ref->modified = true;
    107 }
    108 
    109 /*
    110 ** VID_GetModeInfo
    111 */
    112 typedef struct vidmode_s
    113 {
    114 	const char *description;
    115 	int         width, height;
    116 	int         mode;
    117 } vidmode_t;
    118 
    119 vidmode_t vid_modes[] =
    120 {
    121 	{ "Mode 0: 320x240",   320, 240,   0 },
    122 	{ "Mode 1: 400x300",   400, 300,   1 },
    123 	{ "Mode 2: 512x384",   512, 384,   2 },
    124 	{ "Mode 3: 640x480",   640, 480,   3 },
    125 	{ "Mode 4: 800x600",   800, 600,   4 },
    126 	{ "Mode 5: 960x720",   960, 720,   5 },
    127 	{ "Mode 6: 1024x768",  1024, 768,  6 },
    128 	{ "Mode 7: 1152x864",  1152, 864,  7 },
    129 	{ "Mode 8: 1280x1024",  1280, 1024, 8 },
    130 	{ "Mode 9: 1600x1200", 1600, 1200, 9 }
    131 };
    132 
    133 qboolean VID_GetModeInfo( int *width, int *height, int mode )
    134 {
    135 	if ( mode < 0 || mode >= VID_NUM_MODES )
    136 		return false;
    137 
    138 	*width  = vid_modes[mode].width;
    139 	*height = vid_modes[mode].height;
    140 
    141 	return true;
    142 }
    143 
    144 /*
    145 ** VID_NewWindow
    146 */
    147 void VID_NewWindow ( int width, int height)
    148 {
    149 	viddef.width  = width;
    150 	viddef.height = height;
    151 }
    152 
    153 void VID_FreeReflib (void)
    154 {
    155 	if (reflib_library) {
    156 		if (KBD_Close_fp)
    157 			KBD_Close_fp();
    158 		if (RW_IN_Shutdown_fp)
    159 			RW_IN_Shutdown_fp();
    160 		dlclose(reflib_library);
    161 	}
    162 
    163 	KBD_Init_fp = NULL;
    164 	KBD_Update_fp = NULL;
    165 	KBD_Close_fp = NULL;
    166 	RW_IN_Init_fp = NULL;
    167 	RW_IN_Shutdown_fp = NULL;
    168 	RW_IN_Activate_fp = NULL;
    169 	RW_IN_Commands_fp = NULL;
    170 	RW_IN_Move_fp = NULL;
    171 	RW_IN_Frame_fp = NULL;
    172 
    173 	memset (&re, 0, sizeof(re));
    174 	reflib_library = NULL;
    175 	reflib_active  = false;
    176 }
    177 
    178 /*
    179 ==============
    180 VID_LoadRefresh
    181 ==============
    182 */
    183 qboolean VID_LoadRefresh( char *name )
    184 {
    185 	refimport_t	ri;
    186 	GetRefAPI_t	GetRefAPI;
    187 	char	fn[MAX_OSPATH];
    188 	struct stat st;
    189 	extern uid_t saved_euid;
    190 	FILE *fp;
    191 	
    192 	if ( reflib_active )
    193 	{
    194 		if (KBD_Close_fp)
    195 			KBD_Close_fp();
    196 		if (RW_IN_Shutdown_fp)
    197 			RW_IN_Shutdown_fp();
    198 		KBD_Close_fp = NULL;
    199 		RW_IN_Shutdown_fp = NULL;
    200 		re.Shutdown();
    201 		VID_FreeReflib ();
    202 	}
    203 
    204 	Com_Printf( "------- Loading %s -------\n", name );
    205 
    206 	//regain root
    207 	seteuid(saved_euid);
    208 
    209 	if ((fp = fopen(SO_FILE, "r")) == NULL) {
    210 		Com_Printf( "LoadLibrary(\"%s\") failed: can't open " SO_FILE " (required for location of ref libraries)\n", name);
    211 		return false;
    212 	}
    213 	fgets(fn, sizeof(fn), fp);
    214 	fclose(fp);
    215 	if (*fn && fn[strlen(fn) - 1] == '\n')
    216 		fn[strlen(fn) - 1] = 0;
    217 
    218 	strcat(fn, "/");
    219 	strcat(fn, name);
    220 
    221 	// permission checking
    222 	if (strstr(fn, "softx") == NULL) { // softx doesn't require root
    223 		if (stat(fn, &st) == -1) {
    224 			Com_Printf( "LoadLibrary(\"%s\") failed: %s\n", name, strerror(errno));
    225 			return false;
    226 		}
    227 		if (st.st_uid != 0) {
    228 			Com_Printf( "LoadLibrary(\"%s\") failed: ref is not owned by root\n", name);
    229 			return false;
    230 		}
    231 #if 0
    232 		if ((st.st_mode & 0777) & ~0700) {
    233 			Com_Printf( "LoadLibrary(\"%s\") failed: invalid permissions, must be 700 for security considerations\n", name);
    234 			return false;
    235 		}
    236 #endif
    237 	} else {
    238 		// softx requires we give up root now
    239 		setreuid(getuid(), getuid());
    240 		setegid(getgid());
    241 	}
    242 
    243 	if ( ( reflib_library = dlopen( fn, RTLD_NOW ) ) == 0 )
    244 	{
    245 		Com_Printf( "LoadLibrary(\"%s\") failed: %s\n", name , dlerror());
    246 		return false;
    247 	}
    248 
    249 	ri.Cmd_AddCommand = Cmd_AddCommand;
    250 	ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
    251 	ri.Cmd_Argc = Cmd_Argc;
    252 	ri.Cmd_Argv = Cmd_Argv;
    253 	ri.Cmd_ExecuteText = Cbuf_ExecuteText;
    254 	ri.Con_Printf = VID_Printf;
    255 	ri.Sys_Error = VID_Error;
    256 	ri.FS_LoadFile = FS_LoadFile;
    257 	ri.FS_FreeFile = FS_FreeFile;
    258 	ri.FS_Gamedir = FS_Gamedir;
    259 	ri.Cvar_Get = Cvar_Get;
    260 	ri.Cvar_Set = Cvar_Set;
    261 	ri.Cvar_SetValue = Cvar_SetValue;
    262 	ri.Vid_GetModeInfo = VID_GetModeInfo;
    263 	ri.Vid_MenuInit = VID_MenuInit;
    264 	ri.Vid_NewWindow = VID_NewWindow;
    265 
    266 	if ( ( GetRefAPI = (void *) dlsym( reflib_library, "GetRefAPI" ) ) == 0 )
    267 		Com_Error( ERR_FATAL, "dlsym failed on %s", name );
    268 
    269 	re = GetRefAPI( ri );
    270 
    271 	if (re.api_version != API_VERSION)
    272 	{
    273 		VID_FreeReflib ();
    274 		Com_Error (ERR_FATAL, "%s has incompatible api_version", name);
    275 	}
    276 
    277 	/* Init IN (Mouse) */
    278 	in_state.IN_CenterView_fp = IN_CenterView;
    279 	in_state.Key_Event_fp = Do_Key_Event;
    280 	in_state.viewangles = cl.viewangles;
    281 	in_state.in_strafe_state = &in_strafe.state;
    282 
    283 	if ((RW_IN_Init_fp = dlsym(reflib_library, "RW_IN_Init")) == NULL ||
    284 		(RW_IN_Shutdown_fp = dlsym(reflib_library, "RW_IN_Shutdown")) == NULL ||
    285 		(RW_IN_Activate_fp = dlsym(reflib_library, "RW_IN_Activate")) == NULL ||
    286 		(RW_IN_Commands_fp = dlsym(reflib_library, "RW_IN_Commands")) == NULL ||
    287 		(RW_IN_Move_fp = dlsym(reflib_library, "RW_IN_Move")) == NULL ||
    288 		(RW_IN_Frame_fp = dlsym(reflib_library, "RW_IN_Frame")) == NULL)
    289 		Sys_Error("No RW_IN functions in REF.\n");
    290 
    291 	Real_IN_Init();
    292 
    293 	if ( re.Init( 0, 0 ) == -1 )
    294 	{
    295 		re.Shutdown();
    296 		VID_FreeReflib ();
    297 		return false;
    298 	}
    299 
    300 	/* Init KBD */
    301 #if 1
    302 	if ((KBD_Init_fp = dlsym(reflib_library, "KBD_Init")) == NULL ||
    303 		(KBD_Update_fp = dlsym(reflib_library, "KBD_Update")) == NULL ||
    304 		(KBD_Close_fp = dlsym(reflib_library, "KBD_Close")) == NULL)
    305 		Sys_Error("No KBD functions in REF.\n");
    306 #else
    307 	{
    308 		void KBD_Init(void);
    309 		void KBD_Update(void);
    310 		void KBD_Close(void);
    311 
    312 		KBD_Init_fp = KBD_Init;
    313 		KBD_Update_fp = KBD_Update;
    314 		KBD_Close_fp = KBD_Close;
    315 	}
    316 #endif
    317 	KBD_Init_fp(Do_Key_Event);
    318 
    319 	// give up root now
    320 	setreuid(getuid(), getuid());
    321 	setegid(getgid());
    322 
    323 	Com_Printf( "------------------------------------\n");
    324 	reflib_active = true;
    325 	return true;
    326 }
    327 
    328 /*
    329 ============
    330 VID_CheckChanges
    331 
    332 This function gets called once just before drawing each frame, and it's sole purpose in life
    333 is to check to see if any of the video mode parameters have changed, and if they have to 
    334 update the rendering DLL and/or video mode to match.
    335 ============
    336 */
    337 void VID_CheckChanges (void)
    338 {
    339 	char name[100];
    340 	cvar_t *sw_mode;
    341 
    342 	if ( vid_ref->modified )
    343 	{
    344 		S_StopAllSounds();
    345 	}
    346 
    347 	while (vid_ref->modified)
    348 	{
    349 		/*
    350 		** refresh has changed
    351 		*/
    352 		vid_ref->modified = false;
    353 		vid_fullscreen->modified = true;
    354 		cl.refresh_prepped = false;
    355 		cls.disable_screen = true;
    356 
    357 		sprintf( name, "ref_%s.so", vid_ref->string );
    358 		if ( !VID_LoadRefresh( name ) )
    359 		{
    360 			if ( strcmp (vid_ref->string, "soft") == 0 ||
    361 				strcmp (vid_ref->string, "softx") == 0 ) {
    362 Com_Printf("Refresh failed\n");
    363 				sw_mode = Cvar_Get( "sw_mode", "0", 0 );
    364 				if (sw_mode->value != 0) {
    365 Com_Printf("Trying mode 0\n");
    366 					Cvar_SetValue("sw_mode", 0);
    367 					if ( !VID_LoadRefresh( name ) )
    368 						Com_Error (ERR_FATAL, "Couldn't fall back to software refresh!");
    369 				} else
    370 					Com_Error (ERR_FATAL, "Couldn't fall back to software refresh!");
    371 			}
    372 
    373 			Cvar_Set( "vid_ref", "soft" );
    374 
    375 			/*
    376 			** drop the console if we fail to load a refresh
    377 			*/
    378 			if ( cls.key_dest != key_console )
    379 			{
    380 				Con_ToggleConsole_f();
    381 			}
    382 		}
    383 		cls.disable_screen = false;
    384 	}
    385 
    386 }
    387 
    388 /*
    389 ============
    390 VID_Init
    391 ============
    392 */
    393 void VID_Init (void)
    394 {
    395 	/* Create the video variables so we know how to start the graphics drivers */
    396 	// if DISPLAY is defined, try X
    397 	if (getenv("DISPLAY"))
    398 		vid_ref = Cvar_Get ("vid_ref", "softx", CVAR_ARCHIVE);
    399 	else
    400 		vid_ref = Cvar_Get ("vid_ref", "soft", CVAR_ARCHIVE);
    401 	vid_xpos = Cvar_Get ("vid_xpos", "3", CVAR_ARCHIVE);
    402 	vid_ypos = Cvar_Get ("vid_ypos", "22", CVAR_ARCHIVE);
    403 	vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE);
    404 	vid_gamma = Cvar_Get( "vid_gamma", "1", CVAR_ARCHIVE );
    405 
    406 	/* Add some console commands that we want to handle */
    407 	Cmd_AddCommand ("vid_restart", VID_Restart_f);
    408 
    409 	/* Disable the 3Dfx splash screen */
    410 	putenv("FX_GLIDE_NO_SPLASH=0");
    411 		
    412 	/* Start the graphics mode and load refresh DLL */
    413 	VID_CheckChanges();
    414 }
    415 
    416 /*
    417 ============
    418 VID_Shutdown
    419 ============
    420 */
    421 void VID_Shutdown (void)
    422 {
    423 	if ( reflib_active )
    424 	{
    425 		if (KBD_Close_fp)
    426 			KBD_Close_fp();
    427 		if (RW_IN_Shutdown_fp)
    428 			RW_IN_Shutdown_fp();
    429 		KBD_Close_fp = NULL;
    430 		RW_IN_Shutdown_fp = NULL;
    431 		re.Shutdown ();
    432 		VID_FreeReflib ();
    433 	}
    434 }
    435 
    436 
    437 /*****************************************************************************/
    438 /* INPUT                                                                     */
    439 /*****************************************************************************/
    440 
    441 cvar_t	*in_joystick;
    442 
    443 // This if fake, it's acutally done by the Refresh load
    444 void IN_Init (void)
    445 {
    446 	in_joystick	= Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE);
    447 }
    448 
    449 void Real_IN_Init (void)
    450 {
    451 	if (RW_IN_Init_fp)
    452 		RW_IN_Init_fp(&in_state);
    453 }
    454 
    455 void IN_Shutdown (void)
    456 {
    457 	if (RW_IN_Shutdown_fp)
    458 		RW_IN_Shutdown_fp();
    459 }
    460 
    461 void IN_Commands (void)
    462 {
    463 	if (RW_IN_Commands_fp)
    464 		RW_IN_Commands_fp();
    465 }
    466 
    467 void IN_Move (usercmd_t *cmd)
    468 {
    469 	if (RW_IN_Move_fp)
    470 		RW_IN_Move_fp(cmd);
    471 }
    472 
    473 void IN_Frame (void)
    474 {
    475 	if (RW_IN_Frame_fp)
    476 		RW_IN_Frame_fp();
    477 }
    478 
    479 void IN_Activate (qboolean active)
    480 {
    481 	if (RW_IN_Activate_fp)
    482 		RW_IN_Activate_fp(active);
    483 }
    484 
    485 void Do_Key_Event(int key, qboolean down)
    486 {
    487 	Key_Event(key, down, Sys_Milliseconds());
    488 }
    489