Add 'setrenderer sv' for linux, without needing to be started from a terminal.
This commit is contained in:
parent
777b4b1fd9
commit
e82f61256a
5 changed files with 166 additions and 16 deletions
|
@ -73,6 +73,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
static int noconinput = 0;
|
||||
static int nostdout = 0;
|
||||
|
||||
static FILE *dedcon; //replaces our stdin/out when set.
|
||||
static int dedconproc = -1;
|
||||
|
||||
extern int isPlugin;
|
||||
int sys_parentleft;
|
||||
int sys_parenttop;
|
||||
|
@ -86,10 +89,77 @@ static void Sys_InitClock(void);
|
|||
|
||||
qboolean Sys_InitTerminal (void) //we either have one or we don't.
|
||||
{
|
||||
return isatty(STDIN_FILENO);
|
||||
if (isatty(STDIN_FILENO))
|
||||
return true; //already attached to a terminal in some form. don't need another.
|
||||
|
||||
if (dedcon) //already got one open... shouldn't really happen. future paranoia.
|
||||
return true;
|
||||
else
|
||||
{
|
||||
int pty = posix_openpt(O_RDWR|O_NOCTTY);
|
||||
if (pty >= 0)
|
||||
{
|
||||
int fd;
|
||||
char *slavename, window[64], buf[64];
|
||||
grantpt(pty);
|
||||
unlockpt(pty);
|
||||
slavename = ptsname(pty);
|
||||
dedcon = fopen(slavename, "r+e");
|
||||
if (dedcon)
|
||||
{
|
||||
snprintf(buf, sizeof buf, "-S%s/%d", strrchr(slavename,'/')+1, pty);
|
||||
dedconproc = fork();
|
||||
if(!dedconproc)
|
||||
{ //oh hey, we're the child.
|
||||
execlp("xterm", "xterm", buf, (char *)0);
|
||||
_exit(1);
|
||||
}
|
||||
close(pty); //can close it now, we'll keep track of it with our slave fd
|
||||
if (dedconproc >= 0)
|
||||
{ //if the xterm fails, does this EPIPE properly?
|
||||
fgets(window, sizeof window, dedcon);
|
||||
//printf("window: %s\n", window);
|
||||
|
||||
//switch input to non-blocking, so we can actually still do stuff...
|
||||
fd = fileno(dedcon);
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
{
|
||||
extern int epoll_fd;
|
||||
if (epoll_fd >= 0)
|
||||
{
|
||||
struct epoll_event event = {EPOLLIN, {NULL}};
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event);
|
||||
}
|
||||
}
|
||||
#else
|
||||
//FIXME: NET_Sleep needs to wake up on dedcon input
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
//else fork failed.
|
||||
fclose(dedcon);
|
||||
dedcon = NULL;
|
||||
}
|
||||
close(pty);
|
||||
}
|
||||
}
|
||||
return false; //nope, soz
|
||||
}
|
||||
void Sys_CloseTerminal (void)
|
||||
{
|
||||
if (dedcon)
|
||||
{
|
||||
#ifdef HAVE_EPOLL
|
||||
extern int epoll_fd;
|
||||
if (epoll_fd >= 0)
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fileno(dedcon), NULL); //don't get confused if the FD# is later reopened as something else...
|
||||
#endif
|
||||
fclose(dedcon);
|
||||
dedcon = NULL;
|
||||
}
|
||||
dedconproc = -1;
|
||||
}
|
||||
|
||||
void Sys_RecentServer(char *command, char *target, char *title, char *desc)
|
||||
|
@ -186,7 +256,9 @@ void Sys_Printf (char *fmt, ...)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (nostdout)
|
||||
if (dedcon)
|
||||
out = dedcon;
|
||||
else if (nostdout)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
out = stderr;
|
||||
|
@ -1137,6 +1209,27 @@ char *Sys_ConsoleInput(void)
|
|||
static char text[256];
|
||||
char *nl;
|
||||
|
||||
if (dedcon)
|
||||
{
|
||||
int e;
|
||||
if (fgets(text, sizeof(text), dedcon))
|
||||
return text;
|
||||
e = errno;
|
||||
switch(e)
|
||||
{
|
||||
case EAGAIN:
|
||||
case EINTR: //not meant to be blocking, but can still be interrupted.
|
||||
break; //cos we made it non-blocking.
|
||||
case EPIPE: //not seen, but possible I guess.
|
||||
case EIO: //can happen if the other end dies.
|
||||
Sys_CloseTerminal();
|
||||
return "quit"; //kill the server if we were actually using the terminal... or at least try not to run silently.
|
||||
default:
|
||||
Sys_Printf(CON_WARNING "fgets errno %i\n", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (noconinput)
|
||||
return NULL;
|
||||
|
||||
|
@ -1350,6 +1443,29 @@ static void SigCont(int code)
|
|||
fcntl(STDIN_FILENO, F_SETFL, fl | FNDELAY);
|
||||
noconinput &= ~2;
|
||||
}
|
||||
static void SigChldTerminalDied(int code, void *data)
|
||||
{ //our terminal dieded? restart again (this shouldn't loop infinitely...)
|
||||
Sys_CloseTerminal();
|
||||
Cbuf_AddText ("vid_renderer \"\"; vid_restart\n", RESTRICT_LOCAL);
|
||||
}
|
||||
static void SigChld(int code)
|
||||
{
|
||||
int wstat;
|
||||
pid_t pid;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
pid = wait3 (&wstat, WNOHANG, (struct rusage *)NULL);
|
||||
if (pid == -1)
|
||||
return; //error
|
||||
else if (pid == 0)
|
||||
return; //nothing left to report (linux seems to like errors instead)
|
||||
else if (pid == dedconproc)
|
||||
Cmd_AddTimer(0, SigChldTerminalDied, 0, NULL, 0);
|
||||
// else //forked subserver? we use pipes to track when it dies.
|
||||
// printf ("Return code: %d\n", wstat);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
int main (int c, const char **v)
|
||||
{
|
||||
|
@ -1362,7 +1478,7 @@ int main (int c, const char **v)
|
|||
#ifdef _POSIX_C_SOURCE
|
||||
signal(SIGTTIN, SIG_IGN); //have to ignore this if we want to not lock up when running backgrounded.
|
||||
signal(SIGCONT, SigCont);
|
||||
signal(SIGCHLD, SIG_IGN); //mapcluster stuff might leak zombie processes if we don't do this.
|
||||
signal(SIGCHLD, SigChld); //mapcluster stuff might leak zombie processes if we don't do this.
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -254,8 +254,7 @@ void Cmd_AddTimer(float delay, void(*callback)(int iarg, void *data), int iarg,
|
|||
|
||||
n->timer = realtime + delay;
|
||||
|
||||
n->next = cmdtimers;
|
||||
cmdtimers = n;
|
||||
FTE_Atomic_Insert(cmdtimers, n, n->next);
|
||||
}
|
||||
static void Cmd_In_Callback(int iarg, void *data)
|
||||
{
|
||||
|
|
|
@ -6364,6 +6364,9 @@ static int COM_WorkerThread(void *arg)
|
|||
}
|
||||
static void Sys_ErrorThread(void *ctx, void *data, size_t a, size_t b)
|
||||
{
|
||||
if (ctx)
|
||||
COM_WorkerSync_WorkerStopped(ctx, NULL, a, b);
|
||||
|
||||
//posted to main thread from a worker.
|
||||
Sys_Error("%s", (const char*)data);
|
||||
}
|
||||
|
@ -6383,12 +6386,12 @@ void COM_WorkerAbort(char *message)
|
|||
if (com_worker[us].thread && Sys_IsThread(com_worker[us].thread))
|
||||
{
|
||||
group = WG_LOADER;
|
||||
COM_AddWork(WG_MAIN, COM_WorkerSync_WorkerStopped, &com_worker[us], NULL, 0, group);
|
||||
COM_InsertWork(WG_MAIN, Sys_ErrorThread, &com_worker[us], Z_StrDup(message), 0, group);
|
||||
break;
|
||||
}
|
||||
|
||||
//now tell the main thread that it should be crashing, and why.
|
||||
COM_AddWork(WG_MAIN, Sys_ErrorThread, NULL, Z_StrDup(message), 0, 0);
|
||||
if (us == WORKERTHREADS) //don't know who it was.
|
||||
COM_AddWork(WG_MAIN, Sys_ErrorThread, NULL, Z_StrDup(message), 0, 0);
|
||||
|
||||
Sys_ThreadAbort();
|
||||
}
|
||||
|
@ -6416,11 +6419,10 @@ void COM_DestroyWorkerThread(void)
|
|||
|
||||
while(COM_DoWork(WG_LOADER, false)) //finish any work that got posted to it that it neglected to finish.
|
||||
;
|
||||
COM_WorkerFullSync();
|
||||
while(COM_DoWork(WG_MAIN, false))
|
||||
;
|
||||
|
||||
COM_WorkerFullSync();
|
||||
|
||||
for (i = 0; i < WG_COUNT; i++)
|
||||
{
|
||||
if (com_workercondition[i])
|
||||
|
@ -6435,33 +6437,35 @@ void COM_DestroyWorkerThread(void)
|
|||
//Dangerous: stops workers WITHOUT flushing their queue. Be SURE to 'unlock' to start them up again.
|
||||
void COM_WorkerLock(void)
|
||||
{
|
||||
#define NOFLUSH 0x40000000
|
||||
int i;
|
||||
if (!com_liveworkers[WG_LOADER])
|
||||
return; //nothing to do.
|
||||
|
||||
//add a fake worker and ask workers to die
|
||||
//don't let liveworkers become 0 (so the main thread doesn't flush any pending work) and ask workers to die
|
||||
Sys_LockConditional(com_workercondition[WG_LOADER]);
|
||||
com_liveworkers[WG_LOADER] += 1;
|
||||
com_liveworkers[WG_LOADER] |= NOFLUSH;
|
||||
for (i = 0; i < WORKERTHREADS; i++)
|
||||
com_worker[i].request = WR_DIE; //flag them all to die
|
||||
Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); //and make sure they ALL wake up to check their new death values.
|
||||
Sys_UnlockConditional(com_workercondition[WG_LOADER]);
|
||||
|
||||
//wait for the workers to stop (leaving their work, because of our fake worker)
|
||||
while(com_liveworkers[WG_LOADER]>1)
|
||||
while((com_liveworkers[WG_LOADER]&~NOFLUSH)>0)
|
||||
{
|
||||
if (!COM_DoWork(WG_MAIN, false)) //need to check this to know they're done.
|
||||
COM_DoWork(WG_LOADER, false); //might as well, while we're waiting.
|
||||
}
|
||||
|
||||
//remove our fake worker now...
|
||||
//remove our flush-blocker now...
|
||||
Sys_LockConditional(com_workercondition[WG_LOADER]);
|
||||
com_liveworkers[WG_LOADER] -= 1;
|
||||
com_liveworkers[WG_LOADER] &= ~NOFLUSH;
|
||||
Sys_UnlockConditional(com_workercondition[WG_LOADER]);
|
||||
}
|
||||
//called after COM_WorkerLock
|
||||
void COM_WorkerUnlock(void)
|
||||
{
|
||||
qboolean restarted = false;
|
||||
int i;
|
||||
for (i = 0; i < WORKERTHREADS; i++)
|
||||
{
|
||||
|
@ -6473,8 +6477,14 @@ void COM_WorkerUnlock(void)
|
|||
{
|
||||
com_worker[i].request = WR_NONE;
|
||||
com_worker[i].thread = Sys_CreateThread(va("loadworker_%i", i), COM_WorkerThread, &com_worker[i], 0, 256*1024);
|
||||
if (com_worker[i].thread)
|
||||
restarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!restarted)
|
||||
while (COM_DoWork(WG_LOADER, false))
|
||||
;
|
||||
}
|
||||
|
||||
//fully flushes ALL pending work.
|
||||
|
@ -6709,6 +6719,19 @@ static void COM_InitWorkerThread(void)
|
|||
Cvar_ForceCallback(&worker_count);
|
||||
}
|
||||
|
||||
qboolean FTE_AtomicPtr_ConditionalReplace(qint32_t *ptr, qint32_t old, qint32_t new)
|
||||
{
|
||||
Sys_LockMutex(com_resourcemutex);
|
||||
if (*ptr == old)
|
||||
{
|
||||
*ptr = new;
|
||||
Sys_UnlockMutex(com_resourcemutex);
|
||||
return true;
|
||||
}
|
||||
Sys_UnlockMutex(com_resourcemutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
qint32_t FTE_Atomic32Mutex_Add(qint32_t *ptr, qint32_t change)
|
||||
{
|
||||
qint32_t r;
|
||||
|
@ -6724,6 +6747,15 @@ qint32_t FTE_Atomic32Mutex_Add(qint32_t *ptr, qint32_t change)
|
|||
r = (*ptr += change);
|
||||
return r;
|
||||
}
|
||||
qboolean FTE_AtomicPtr_ConditionalReplace(qint32_t *ptr, qint32_t old, qint32_t new)
|
||||
{ //hope it ain't threaded
|
||||
if (*ptr == old)
|
||||
{
|
||||
*ptr = new;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
@ -93,7 +93,7 @@ static cvar_t net_enable_kexlobby = CVARD("net_enable_"KEXLOBBY, "0", "If en
|
|||
//#endif
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
static int epoll_fd = -1;
|
||||
int epoll_fd = -1;
|
||||
#endif
|
||||
|
||||
void NET_GetLocalAddress (int socket, netadr_t *out);
|
||||
|
|
|
@ -108,14 +108,17 @@ qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refres
|
|||
#define qatomic32_t qint32_t
|
||||
#define FTE_Atomic32_Inc(ptr) __sync_add_and_fetch(ptr, 1) //returns the AFTER the operation.
|
||||
#define FTE_Atomic32_Dec(ptr) __sync_add_and_fetch(ptr, -1) //returns the AFTER the operation.
|
||||
#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(!__sync_bool_compare_and_swap(&head, newnodenext, newnode)) //atomically insert into a linked list, being sure to not corrupt the pointers
|
||||
#elif defined(_WIN32)
|
||||
#define qatomic32_t long
|
||||
#define FTE_Atomic32_Inc(ptr) _InterlockedIncrement(ptr)
|
||||
#define FTE_Atomic32_Dec(ptr) _InterlockedDecrement(ptr)
|
||||
#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(newnodenext != _InterlockedCompareExchangePointer(&head, newnode, newnodenext))
|
||||
#else
|
||||
#define qatomic32_t qint32_t
|
||||
#define FTE_Atomic32_Inc(ptr) FTE_Atomic32Mutex_Add(ptr, 1)
|
||||
#define FTE_Atomic32_Dec(ptr) FTE_Atomic32Mutex_Add(ptr, -1)
|
||||
#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(!FTE_AtomicPtr_ConditionalReplace(&head, newnodenext, newnode))
|
||||
#endif
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue