Add 'setrenderer sv' for linux, without needing to be started from a terminal.

This commit is contained in:
Shpoike 2024-11-08 14:43:32 +00:00
parent 777b4b1fd9
commit e82f61256a
5 changed files with 166 additions and 16 deletions

View file

@ -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

View file

@ -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)
{

View file

@ -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
/*

View file

@ -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);

View file

@ -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