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