📄 unix_main.c
字号:
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <errno.h>
#ifdef __linux__ // rb010123
#include <mntent.h>
#endif
#include <dlfcn.h>
#ifdef __linux__
#include <fpu_control.h> // bk001213 - force dumps on divide by zero
#endif
// FIXME TTimo should we gard this? most *nix system should comply?
#include <termios.h>
#include "../game/q_shared.h"
#include "../qcommon/qcommon.h"
#include "../renderer/tr_public.h"
#include "linux_local.h" // bk001204
// Structure containing functions exported from refresh DLL
refexport_t re;
unsigned sys_frame_time;
uid_t saved_euid;
qboolean stdin_active = qtrue;
// =============================================================
// tty console variables
// =============================================================
// enable/disabled tty input mode
// NOTE TTimo this is used during startup, cannot be changed during run
static cvar_t *ttycon = NULL;
// general flag to tell about tty console mode
static qboolean ttycon_on = qfalse;
// when printing general stuff to stdout stderr (Sys_Printf)
// we need to disable the tty console stuff
// this increments so we can recursively disable
static int ttycon_hide = 0;
// some key codes that the terminal may be using
// TTimo NOTE: I'm not sure how relevant this is
static int tty_erase;
static int tty_eof;
static struct termios tty_tc;
static field_t tty_con;
// history
// NOTE TTimo this is a bit duplicate of the graphical console history
// but it's safer and faster to write our own here
#define TTY_HISTORY 32
static field_t ttyEditLines[TTY_HISTORY];
static int hist_current = -1, hist_count = 0;
// =======================================================================
// General routines
// =======================================================================
// bk001207
#define MEM_THRESHOLD 96*1024*1024
/*
==================
Sys_LowPhysicalMemory()
==================
*/
qboolean Sys_LowPhysicalMemory() {
//MEMORYSTATUS stat;
//GlobalMemoryStatus (&stat);
//return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse;
return qfalse; // bk001207 - FIXME
}
/*
==================
Sys_FunctionCmp
==================
*/
int Sys_FunctionCmp(void *f1, void *f2) {
return qtrue;
}
/*
==================
Sys_FunctionCheckSum
==================
*/
int Sys_FunctionCheckSum(void *f1) {
return 0;
}
/*
==================
Sys_MonkeyShouldBeSpanked
==================
*/
int Sys_MonkeyShouldBeSpanked( void ) {
return 0;
}
void Sys_BeginProfiling( void ) {
}
/*
=================
Sys_In_Restart_f
Restart the input subsystem
=================
*/
void Sys_In_Restart_f( void )
{
IN_Shutdown();
IN_Init();
}
// =============================================================
// tty console routines
// NOTE: if the user is editing a line when something gets printed to the early console then it won't look good
// so we provide tty_Clear and tty_Show to be called before and after a stdout or stderr output
// =============================================================
// flush stdin, I suspect some terminals are sending a LOT of shit
// FIXME TTimo relevant?
void tty_FlushIn()
{
char key;
while (read(0, &key, 1)!=-1);
}
// do a backspace
// TTimo NOTE: it seems on some terminals just sending '\b' is not enough
// so for now, in any case we send "\b \b" .. yeah well ..
// (there may be a way to find out if '\b' alone would work though)
void tty_Back()
{
char key;
key = '\b';
write(1, &key, 1);
key = ' ';
write(1, &key, 1);
key = '\b';
write(1, &key, 1);
}
// clear the display of the line currently edited
// bring cursor back to beginning of line
void tty_Hide()
{
int i;
assert(ttycon_on);
if (ttycon_hide)
{
ttycon_hide++;
return;
}
if (tty_con.cursor>0)
{
for (i=0; i<tty_con.cursor; i++)
{
tty_Back();
}
}
ttycon_hide++;
}
// show the current line
// FIXME TTimo need to position the cursor if needed??
void tty_Show()
{
int i;
assert(ttycon_on);
assert(ttycon_hide>0);
ttycon_hide--;
if (ttycon_hide == 0)
{
if (tty_con.cursor)
{
for (i=0; i<tty_con.cursor; i++)
{
write(1, tty_con.buffer+i, 1);
}
}
}
}
// never exit without calling this, or your terminal will be left in a pretty bad state
void Sys_ConsoleInputShutdown()
{
if (ttycon_on)
{
Com_Printf("Shutdown tty console\n");
tcsetattr (0, TCSADRAIN, &tty_tc);
}
}
void Hist_Add(field_t *field)
{
int i;
assert(hist_count <= TTY_HISTORY);
assert(hist_count >= 0);
assert(hist_current >= -1);
assert(hist_current <= hist_count);
// make some room
for (i=TTY_HISTORY-1; i>0; i--)
{
ttyEditLines[i] = ttyEditLines[i-1];
}
ttyEditLines[0] = *field;
if (hist_count<TTY_HISTORY)
{
hist_count++;
}
hist_current = -1; // re-init
}
field_t *Hist_Prev()
{
int hist_prev;
assert(hist_count <= TTY_HISTORY);
assert(hist_count >= 0);
assert(hist_current >= -1);
assert(hist_current <= hist_count);
hist_prev = hist_current + 1;
if (hist_prev >= hist_count)
{
return NULL;
}
hist_current++;
return &(ttyEditLines[hist_current]);
}
field_t *Hist_Next()
{
assert(hist_count <= TTY_HISTORY);
assert(hist_count >= 0);
assert(hist_current >= -1);
assert(hist_current <= hist_count);
if (hist_current >= 0)
{
hist_current--;
}
if (hist_current == -1)
{
return NULL;
}
return &(ttyEditLines[hist_current]);
}
// =============================================================
// general sys routines
// =============================================================
#if 0
// NOTE TTimo this is not used .. looks interesting though? protection against buffer overflow kind of stuff?
void Sys_Printf (char *fmt, ...)
{
va_list argptr;
char text[1024];
unsigned char *p;
va_start (argptr,fmt);
vsprintf (text,fmt,argptr);
va_end (argptr);
if (strlen(text) > sizeof(text))
Sys_Error("memory overwrite in Sys_Printf");
for (p = (unsigned char *)text; *p; p++)
{
*p &= 0x7f;
if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
printf("[%02x]", *p);
else
putc(*p, stdout);
}
}
#endif
// single exit point (regular exit or in case of signal fault)
void Sys_Exit( int ex ) {
Sys_ConsoleInputShutdown();
#ifdef NDEBUG // regular behavior
// We can't do this
// as long as GL DLL's keep installing with atexit...
//exit(ex);
_exit(ex);
#else
// Give me a backtrace on error exits.
assert( ex == 0 );
exit(ex);
#endif
}
void Sys_Quit (void) {
CL_Shutdown ();
fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
Sys_Exit(0);
}
void Sys_Init(void)
{
Cmd_AddCommand ("in_restart", Sys_In_Restart_f);
#if defined __linux__
#if defined __i386__
Cvar_Set( "arch", "linux i386" );
#elif defined __alpha__
Cvar_Set( "arch", "linux alpha" );
#elif defined __sparc__
Cvar_Set( "arch", "linux sparc" );
#elif defined __FreeBSD__
#if defined __i386__ // FreeBSD
Cvar_Set( "arch", "freebsd i386" );
#elif defined __alpha__
Cvar_Set( "arch", "freebsd alpha" );
#else
Cvar_Set( "arch", "freebsd unknown" );
#endif // FreeBSD
#else
Cvar_Set( "arch", "linux unknown" );
#endif
#elif defined __sun__
#if defined __i386__
Cvar_Set( "arch", "solaris x86" );
#elif defined __sparc__
Cvar_Set( "arch", "solaris sparc" );
#else
Cvar_Set( "arch", "solaris unknown" );
#endif
#elif defined __sgi__
#if defined __mips__
Cvar_Set( "arch", "sgi mips" );
#else
Cvar_Set( "arch", "sgi unknown" );
#endif
#else
Cvar_Set( "arch", "unknown" );
#endif
Cvar_Set( "username", Sys_GetCurrentUser() );
IN_Init();
}
void Sys_Error( const char *error, ...)
{
va_list argptr;
char string[1024];
// change stdin to non blocking
// NOTE TTimo not sure how well that goes with tty console mode
fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
// don't bother do a show on this one heh
if (ttycon_on)
{
tty_Hide();
}
CL_Shutdown ();
va_start (argptr,error);
vsprintf (string,error,argptr);
va_end (argptr);
fprintf(stderr, "Sys_Error: %s\n", string);
Sys_Exit( 1 ); // bk010104 - use single exit point.
}
void Sys_Warn (char *warning, ...)
{
va_list argptr;
char string[1024];
va_start (argptr,warning);
vsprintf (string,warning,argptr);
va_end (argptr);
if (ttycon_on)
{
tty_Hide();
}
fprintf(stderr, "Warning: %s", string);
if (ttycon_on)
{
tty_Show();
}
}
/*
============
Sys_FileTime
returns -1 if not present
============
*/
int Sys_FileTime (char *path)
{
struct stat buf;
if (stat (path,&buf) == -1)
return -1;
return buf.st_mtime;
}
void floating_point_exception_handler(int whatever)
{
signal(SIGFPE, floating_point_exception_handler);
}
// initialize the console input (tty mode if wanted and possible)
void Sys_ConsoleInputInit()
{
struct termios tc;
// TTimo
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390
// ttycon 0 or 1, if the process is backgrounded (running non interactively)
// then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
// FIXME TTimo initialize this in Sys_Init or something?
ttycon = Cvar_Get("ttycon", "1", 0);
if (ttycon && ttycon->value)
{
if (isatty(STDIN_FILENO)!=1)
{
Com_Printf("stdin is not a tty, tty console mode failed\n");
Cvar_Set("ttycon", "0");
ttycon_on = qfalse;
return;
}
Com_Printf("Started tty console (use +set ttycon 0 to disable)\n");
Field_Clear(&tty_con);
tcgetattr (0, &tty_tc);
tty_erase = tty_tc.c_cc[VERASE];
tty_eof = tty_tc.c_cc[VEOF];
tc = tty_tc;
/*
ECHO: don't echo input characters
ICANON: enable canonical mode. This enables the special
characters EOF, EOL, EOL2, ERASE, KILL, REPRINT,
STATUS, and WERASE, and buffers by lines.
ISIG: when any of the characters INTR, QUIT, SUSP, or
DSUSP are received, generate the corresponding sig
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -