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