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