mirror of
https://git.do.srb2.org/KartKrew/Kart-Public.git
synced 2025-01-31 21:40:56 +00:00
a8e0805261
Should fix problems where Switch Pro controllers suddenly had their mappings changed. Does not recognize controllers connected before game launch. Make sure to replug your controller after the game launches if using this param.
3890 lines
92 KiB
C
3890 lines
92 KiB
C
// Emacs style mode select -*- C++ -*-
|
|
//
|
|
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
// Portions Copyright (C) 1998-2000 by DooM Legacy Team.
|
|
// Copyright (C) 2014-2018 by Sonic Team Junior.
|
|
//
|
|
// This program 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.
|
|
//
|
|
// This program 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.
|
|
//
|
|
// Changes by Graue <graue@oceanbase.org> are in the public domain.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
/// \file
|
|
/// \brief SRB2 system stuff for SDL
|
|
|
|
#ifdef CMAKECONFIG
|
|
#include "config.h"
|
|
#else
|
|
#include "../config.h.in"
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
|
|
#ifdef _WIN32
|
|
#define RPC_NO_WINDOWS_H
|
|
#include <windows.h>
|
|
#include "../doomtype.h"
|
|
typedef BOOL (WINAPI *p_GetDiskFreeSpaceExA)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
|
|
typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD);
|
|
typedef DWORD (WINAPI *p_timeGetTime) (void);
|
|
typedef UINT (WINAPI *p_timeEndPeriod) (UINT);
|
|
typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR);
|
|
typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifdef __GNUC__
|
|
#include <unistd.h>
|
|
#elif defined (_MSC_VER)
|
|
#include <direct.h>
|
|
#endif
|
|
#if defined (__unix__) || defined (UNIXCOMMON)
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#ifdef _WIN32
|
|
#include <conio.h>
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable : 4214 4244)
|
|
#endif
|
|
|
|
#ifdef HAVE_SDL
|
|
#define _MATH_DEFINES_DEFINED
|
|
#include "SDL.h"
|
|
|
|
#ifdef HAVE_TTF
|
|
#include "i_ttf.h"
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(default : 4214 4244)
|
|
#endif
|
|
|
|
#include "SDL_cpuinfo.h"
|
|
#define HAVE_SDLCPUINFO
|
|
|
|
#if defined (__unix__) || defined(__APPLE__) || (defined (UNIXCOMMON) && !defined (__HAIKU__))
|
|
#if defined (__linux__)
|
|
#include <sys/vfs.h>
|
|
#else
|
|
#include <sys/param.h>
|
|
#include <sys/mount.h>
|
|
/*For meminfo*/
|
|
#include <sys/types.h>
|
|
#ifdef FREEBSD
|
|
#include <kvm.h>
|
|
#endif
|
|
#include <nlist.h>
|
|
#include <sys/vmmeter.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined (__linux__) || (defined (UNIXCOMMON) && !defined (__HAIKU__))
|
|
#ifndef NOTERMIOS
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h> // ioctl
|
|
#define HAVE_TERMIOS
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef NOMUMBLE
|
|
#ifdef __linux__ // need -lrt
|
|
#include <sys/mman.h>
|
|
#ifdef MAP_FAILED
|
|
#define HAVE_SHM
|
|
#endif
|
|
#include <wchar.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#define HAVE_MUMBLE
|
|
#define WINMUMBLE
|
|
#elif defined (HAVE_SHM)
|
|
#define HAVE_MUMBLE
|
|
#endif
|
|
#endif // NOMUMBLE
|
|
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
#include "macosx/mac_resources.h"
|
|
#endif
|
|
|
|
#ifndef errno
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
// Locations for searching the srb2.srb
|
|
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
|
|
#define DEFAULTWADLOCATION1 "/usr/local/share/games/SRB2Kart"
|
|
#define DEFAULTWADLOCATION2 "/usr/local/games/SRB2Kart"
|
|
#define DEFAULTWADLOCATION3 "/usr/share/games/SRB2Kart"
|
|
#define DEFAULTWADLOCATION4 "/usr/games/SRB2Kart"
|
|
#define DEFAULTSEARCHPATH1 "/usr/local/games"
|
|
#define DEFAULTSEARCHPATH2 "/usr/games"
|
|
#define DEFAULTSEARCHPATH3 "/usr/local"
|
|
#elif defined (_WIN32)
|
|
#define DEFAULTWADLOCATION1 "c:\\games\\srb2kart"
|
|
#define DEFAULTWADLOCATION2 "\\games\\srb2kart"
|
|
#define DEFAULTSEARCHPATH1 "c:\\games"
|
|
#define DEFAULTSEARCHPATH2 "\\games"
|
|
#endif
|
|
|
|
/** \brief WAD file to look for
|
|
*/
|
|
#define WADKEYWORD1 "srb2.srb"
|
|
#define WADKEYWORD2 "srb2.wad"
|
|
/** \brief holds wad path
|
|
*/
|
|
static char returnWadPath[256];
|
|
|
|
//Alam_GBC: SDL
|
|
|
|
#include "../doomdef.h"
|
|
#include "../m_misc.h"
|
|
#include "../i_video.h"
|
|
#include "../i_sound.h"
|
|
#include "../i_system.h"
|
|
#include "../screen.h" //vid.WndParent
|
|
#include "../d_net.h"
|
|
#include "../g_game.h"
|
|
#include "../filesrch.h"
|
|
#include "endtxt.h"
|
|
#include "sdlmain.h"
|
|
|
|
#include "../i_joy.h"
|
|
|
|
#include "../m_argv.h"
|
|
|
|
#ifdef MAC_ALERT
|
|
#include "macosx/mac_alert.h"
|
|
#endif
|
|
|
|
#include "../d_main.h"
|
|
|
|
#if !defined(NOMUMBLE) && defined(HAVE_MUMBLE)
|
|
// Mumble context string
|
|
#include "../d_clisrv.h"
|
|
#include "../byteptr.h"
|
|
#endif
|
|
|
|
/** \brief The JoyReset function
|
|
|
|
\param JoySet Joystick info to reset
|
|
|
|
\return void
|
|
*/
|
|
static void JoyReset(SDLJoyInfo_t *JoySet)
|
|
{
|
|
if (JoySet->dev)
|
|
{
|
|
SDL_JoystickClose(JoySet->dev);
|
|
}
|
|
JoySet->dev = NULL;
|
|
JoySet->oldjoy = -1;
|
|
JoySet->axises = JoySet->buttons = JoySet->hats = JoySet->balls = 0;
|
|
//JoySet->scale
|
|
}
|
|
|
|
/** \brief First joystick up and running
|
|
*/
|
|
static INT32 joystick_started = 0;
|
|
|
|
/** \brief SDL info about joystick 1
|
|
*/
|
|
SDLJoyInfo_t JoyInfo;
|
|
|
|
|
|
/** \brief Second joystick up and running
|
|
*/
|
|
static INT32 joystick2_started = 0;
|
|
|
|
/** \brief SDL info about joystick 2
|
|
*/
|
|
SDLJoyInfo_t JoyInfo2;
|
|
|
|
/** \brief Third joystick up and running
|
|
*/
|
|
static INT32 joystick3_started = 0;
|
|
|
|
/** \brief SDL info about joystick 3
|
|
*/
|
|
SDLJoyInfo_t JoyInfo3;
|
|
|
|
/** \brief Fourth joystick up and running
|
|
*/
|
|
static INT32 joystick4_started = 0;
|
|
|
|
/** \brief SDL info about joystick 4
|
|
*/
|
|
SDLJoyInfo_t JoyInfo4;
|
|
|
|
#ifdef HAVE_TERMIOS
|
|
static INT32 fdmouse2 = -1;
|
|
static INT32 mouse2_started = 0;
|
|
#endif
|
|
|
|
SDL_bool consolevent = SDL_FALSE;
|
|
SDL_bool framebuffer = SDL_FALSE;
|
|
|
|
UINT8 keyboard_started = false;
|
|
|
|
FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
|
|
{
|
|
//static char msg[] = "oh no! back to reality!\r\n";
|
|
const char * sigmsg;
|
|
char sigdef[32];
|
|
|
|
D_QuitNetGame(); // Fix server freezes
|
|
|
|
switch (num)
|
|
{
|
|
// case SIGINT:
|
|
// sigmsg = "SIGINT - interrupted";
|
|
// break;
|
|
case SIGILL:
|
|
sigmsg = "SIGILL - illegal instruction - invalid function image";
|
|
break;
|
|
case SIGFPE:
|
|
sigmsg = "SIGFPE - mathematical exception";
|
|
break;
|
|
case SIGSEGV:
|
|
sigmsg = "SIGSEGV - segment violation";
|
|
break;
|
|
// case SIGTERM:
|
|
// sigmsg = "SIGTERM - Software termination signal from kill";
|
|
// break;
|
|
// case SIGBREAK:
|
|
// sigmsg = "SIGBREAK - Ctrl-Break sequence";
|
|
// break;
|
|
case SIGABRT:
|
|
sigmsg = "SIGABRT - abnormal termination triggered by abort call";
|
|
break;
|
|
default:
|
|
sprintf(sigdef,"signal number %d", num);
|
|
sigmsg = sigdef;
|
|
}
|
|
|
|
I_OutputMsg("\nsignal_handler() error: %s\n", sigmsg);
|
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
|
|
"Signal caught",
|
|
sigmsg, NULL);
|
|
I_ShutdownSystem();
|
|
signal(num, SIG_DFL); //default signal action
|
|
raise(num);
|
|
I_Quit();
|
|
}
|
|
|
|
FUNCNORETURN static ATTRNORETURN void quit_handler(int num)
|
|
{
|
|
signal(num, SIG_DFL); //default signal action
|
|
raise(num);
|
|
I_Quit();
|
|
}
|
|
|
|
#ifdef HAVE_TERMIOS
|
|
// TERMIOS console code from Quake3: thank you!
|
|
SDL_bool stdin_active = SDL_TRUE;
|
|
|
|
typedef struct
|
|
{
|
|
size_t cursor;
|
|
char buffer[256];
|
|
} feild_t;
|
|
|
|
feild_t tty_con;
|
|
|
|
// 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 INT32 ttycon_hide = 0;
|
|
// some key codes that the terminal may be using
|
|
// TTimo NOTE: I'm not sure how relevant this is
|
|
static INT32 tty_erase;
|
|
static INT32 tty_eof;
|
|
|
|
static struct termios tty_tc;
|
|
|
|
// =============================================================
|
|
// 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 garbage
|
|
// FIXME TTimo relevant?
|
|
#if 0
|
|
static inline void tty_FlushIn(void)
|
|
{
|
|
char key;
|
|
while (read(STDIN_FILENO, &key, 1)!=-1);
|
|
}
|
|
#endif
|
|
|
|
// 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)
|
|
static void tty_Back(void)
|
|
{
|
|
char key;
|
|
ssize_t d;
|
|
key = '\b';
|
|
d = write(STDOUT_FILENO, &key, 1);
|
|
key = ' ';
|
|
d = write(STDOUT_FILENO, &key, 1);
|
|
key = '\b';
|
|
d = write(STDOUT_FILENO, &key, 1);
|
|
(void)d;
|
|
}
|
|
|
|
static void tty_Clear(void)
|
|
{
|
|
size_t i;
|
|
if (tty_con.cursor>0)
|
|
{
|
|
for (i=0; i<tty_con.cursor; i++)
|
|
{
|
|
tty_Back();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// clear the display of the line currently edited
|
|
// bring cursor back to beginning of line
|
|
static inline void tty_Hide(void)
|
|
{
|
|
//I_Assert(consolevent);
|
|
if (ttycon_hide)
|
|
{
|
|
ttycon_hide++;
|
|
return;
|
|
}
|
|
tty_Clear();
|
|
ttycon_hide++;
|
|
}
|
|
|
|
// show the current line
|
|
// FIXME TTimo need to position the cursor if needed??
|
|
static inline void tty_Show(void)
|
|
{
|
|
size_t i;
|
|
ssize_t d;
|
|
//I_Assert(consolevent);
|
|
I_Assert(ttycon_hide>0);
|
|
ttycon_hide--;
|
|
if (ttycon_hide == 0 && tty_con.cursor)
|
|
{
|
|
for (i=0; i<tty_con.cursor; i++)
|
|
{
|
|
d = write(STDOUT_FILENO, tty_con.buffer+i, 1);
|
|
}
|
|
}
|
|
(void)d;
|
|
}
|
|
|
|
// never exit without calling this, or your terminal will be left in a pretty bad state
|
|
static void I_ShutdownConsole(void)
|
|
{
|
|
if (consolevent)
|
|
{
|
|
I_OutputMsg("Shutdown tty console\n");
|
|
consolevent = SDL_FALSE;
|
|
tcsetattr (STDIN_FILENO, TCSADRAIN, &tty_tc);
|
|
}
|
|
}
|
|
|
|
static void I_StartupConsole(void)
|
|
{
|
|
struct termios tc;
|
|
|
|
// TTimo
|
|
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390 (404)
|
|
// then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
|
|
signal(SIGTTIN, SIG_IGN);
|
|
signal(SIGTTOU, SIG_IGN);
|
|
|
|
consolevent = !M_CheckParm("-noconsole");
|
|
framebuffer = M_CheckParm("-framebuffer");
|
|
|
|
if (framebuffer)
|
|
consolevent = SDL_FALSE;
|
|
|
|
if (!consolevent) return;
|
|
|
|
if (isatty(STDIN_FILENO)!=1)
|
|
{
|
|
I_OutputMsg("stdin is not a tty, tty console mode failed\n");
|
|
consolevent = SDL_FALSE;
|
|
return;
|
|
}
|
|
memset(&tty_con, 0x00, sizeof(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 signal
|
|
*/
|
|
tc.c_lflag &= ~(ECHO | ICANON);
|
|
/*
|
|
ISTRIP strip off bit 8
|
|
INPCK enable input parity checking
|
|
*/
|
|
tc.c_iflag &= ~(ISTRIP | INPCK);
|
|
tc.c_cc[VMIN] = 0; //1?
|
|
tc.c_cc[VTIME] = 0;
|
|
tcsetattr (0, TCSADRAIN, &tc);
|
|
}
|
|
|
|
void I_GetConsoleEvents(void)
|
|
{
|
|
// we use this when sending back commands
|
|
event_t ev = {0,0,0,0};
|
|
char key = 0;
|
|
ssize_t d;
|
|
|
|
if (!consolevent)
|
|
return;
|
|
|
|
ev.type = ev_console;
|
|
if (read(STDIN_FILENO, &key, 1) == -1 || !key)
|
|
return;
|
|
|
|
// we have something
|
|
// backspace?
|
|
// NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
|
|
if ((key == tty_erase) || (key == 127) || (key == 8))
|
|
{
|
|
if (tty_con.cursor > 0)
|
|
{
|
|
tty_con.cursor--;
|
|
tty_con.buffer[tty_con.cursor] = '\0';
|
|
tty_Back();
|
|
}
|
|
ev.data1 = KEY_BACKSPACE;
|
|
}
|
|
else if (key < ' ') // check if this is a control char
|
|
{
|
|
if (key == '\n')
|
|
{
|
|
tty_Clear();
|
|
tty_con.cursor = 0;
|
|
ev.data1 = KEY_ENTER;
|
|
}
|
|
else return;
|
|
}
|
|
else
|
|
{
|
|
// push regular character
|
|
ev.data1 = tty_con.buffer[tty_con.cursor] = key;
|
|
tty_con.cursor++;
|
|
// print the current line (this is differential)
|
|
d = write(STDOUT_FILENO, &key, 1);
|
|
}
|
|
if (ev.data1) D_PostEvent(&ev);
|
|
//tty_FlushIn();
|
|
(void)d;
|
|
}
|
|
|
|
#elif defined (_WIN32)
|
|
static BOOL I_ReadyConsole(HANDLE ci)
|
|
{
|
|
DWORD gotinput;
|
|
if (ci == INVALID_HANDLE_VALUE) return FALSE;
|
|
if (WaitForSingleObject(ci,0) != WAIT_OBJECT_0) return FALSE;
|
|
if (GetFileType(ci) != FILE_TYPE_CHAR) return FALSE;
|
|
if (!GetConsoleMode(ci, &gotinput)) return FALSE;
|
|
return (GetNumberOfConsoleInputEvents(ci, &gotinput) && gotinput);
|
|
}
|
|
|
|
static boolean entering_con_command = false;
|
|
|
|
static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co)
|
|
{
|
|
event_t event;
|
|
CONSOLE_SCREEN_BUFFER_INFO CSBI;
|
|
DWORD t;
|
|
|
|
memset(&event,0x00,sizeof (event));
|
|
|
|
if (evt.bKeyDown)
|
|
{
|
|
event.type = ev_console;
|
|
entering_con_command = true;
|
|
switch (evt.wVirtualKeyCode)
|
|
{
|
|
case VK_ESCAPE:
|
|
case VK_TAB:
|
|
event.data1 = KEY_NULL;
|
|
break;
|
|
case VK_SHIFT:
|
|
event.data1 = KEY_LSHIFT;
|
|
break;
|
|
case VK_RETURN:
|
|
entering_con_command = false;
|
|
// Fall through.
|
|
default:
|
|
event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char
|
|
}
|
|
if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t))
|
|
{
|
|
if (event.data1 && event.data1 != KEY_LSHIFT && event.data1 != KEY_RSHIFT)
|
|
{
|
|
#ifdef _UNICODE
|
|
WriteConsole(co, &evt.uChar.UnicodeChar, 1, &t, NULL);
|
|
#else
|
|
WriteConsole(co, &evt.uChar.AsciiChar, 1 , &t, NULL);
|
|
#endif
|
|
}
|
|
if (evt.wVirtualKeyCode == VK_BACK
|
|
&& GetConsoleScreenBufferInfo(co,&CSBI))
|
|
{
|
|
WriteConsoleOutputCharacterA(co, " ",1, CSBI.dwCursorPosition, &t);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
event.type = ev_keyup;
|
|
switch (evt.wVirtualKeyCode)
|
|
{
|
|
case VK_SHIFT:
|
|
event.data1 = KEY_LSHIFT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (event.data1) D_PostEvent(&event);
|
|
}
|
|
|
|
void I_GetConsoleEvents(void)
|
|
{
|
|
HANDLE ci = GetStdHandle(STD_INPUT_HANDLE);
|
|
HANDLE co = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
INPUT_RECORD input;
|
|
DWORD t;
|
|
|
|
while (I_ReadyConsole(ci) && ReadConsoleInput(ci, &input, 1, &t) && t)
|
|
{
|
|
switch (input.EventType)
|
|
{
|
|
case KEY_EVENT:
|
|
Impl_HandleKeyboardConsoleEvent(input.Event.KeyEvent, co);
|
|
break;
|
|
case MOUSE_EVENT:
|
|
case WINDOW_BUFFER_SIZE_EVENT:
|
|
case MENU_EVENT:
|
|
case FOCUS_EVENT:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void I_StartupConsole(void)
|
|
{
|
|
HANDLE ci, co;
|
|
const INT32 ded = M_CheckParm("-dedicated");
|
|
BOOL gotConsole = FALSE;
|
|
if (M_CheckParm("-console") || ded)
|
|
gotConsole = AllocConsole();
|
|
#ifdef _DEBUG
|
|
else if (M_CheckParm("-noconsole") && !ded)
|
|
#else
|
|
else if (!M_CheckParm("-console") && !ded)
|
|
#endif
|
|
{
|
|
FreeConsole();
|
|
gotConsole = FALSE;
|
|
}
|
|
|
|
if (gotConsole)
|
|
{
|
|
SetConsoleTitleA("SRB2Kart Console");
|
|
consolevent = SDL_TRUE;
|
|
}
|
|
|
|
//Let get the real console HANDLE, because Mingw's Bash is bad!
|
|
ci = CreateFile(TEXT("CONIN$") , GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
co = CreateFile(TEXT("CONOUT$"), GENERIC_WRITE|GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (ci != INVALID_HANDLE_VALUE)
|
|
{
|
|
const DWORD CM = ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT;
|
|
SetStdHandle(STD_INPUT_HANDLE, ci);
|
|
if (GetFileType(ci) == FILE_TYPE_CHAR)
|
|
SetConsoleMode(ci, CM); //default mode but no ENABLE_MOUSE_INPUT
|
|
}
|
|
if (co != INVALID_HANDLE_VALUE)
|
|
{
|
|
SetStdHandle(STD_OUTPUT_HANDLE, co);
|
|
SetStdHandle(STD_ERROR_HANDLE, co);
|
|
}
|
|
}
|
|
static inline void I_ShutdownConsole(void){}
|
|
#else
|
|
void I_GetConsoleEvents(void){}
|
|
static inline void I_StartupConsole(void)
|
|
{
|
|
#ifdef _DEBUG
|
|
consolevent = !M_CheckParm("-noconsole");
|
|
#else
|
|
consolevent = M_CheckParm("-console");
|
|
#endif
|
|
|
|
framebuffer = M_CheckParm("-framebuffer");
|
|
|
|
if (framebuffer)
|
|
consolevent = SDL_FALSE;
|
|
}
|
|
static inline void I_ShutdownConsole(void){}
|
|
#endif
|
|
|
|
//
|
|
// StartupKeyboard
|
|
//
|
|
void I_StartupKeyboard (void)
|
|
{
|
|
#ifdef SIGINT
|
|
signal(SIGINT , quit_handler);
|
|
#endif
|
|
#ifdef SIGBREAK
|
|
signal(SIGBREAK , quit_handler);
|
|
#endif
|
|
#ifdef SIGTERM
|
|
signal(SIGTERM , quit_handler);
|
|
#endif
|
|
|
|
// If these defines don't exist,
|
|
// then compilation would have failed above us...
|
|
signal(SIGILL , signal_handler);
|
|
signal(SIGSEGV , signal_handler);
|
|
signal(SIGABRT , signal_handler);
|
|
signal(SIGFPE , signal_handler);
|
|
}
|
|
|
|
//
|
|
//I_OutputMsg
|
|
//
|
|
void I_OutputMsg(const char *fmt, ...)
|
|
{
|
|
size_t len;
|
|
XBOXSTATIC char txt[8192];
|
|
va_list argptr;
|
|
|
|
va_start(argptr,fmt);
|
|
vsprintf(txt, fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
#ifdef HAVE_TTF
|
|
if (TTF_WasInit()) I_TTFDrawText(currentfont, solid, DEFAULTFONTFGR, DEFAULTFONTFGG, DEFAULTFONTFGB, DEFAULTFONTFGA,
|
|
DEFAULTFONTBGR, DEFAULTFONTBGG, DEFAULTFONTBGB, DEFAULTFONTBGA, txt);
|
|
#endif
|
|
|
|
#if defined (_WIN32) && defined (_MSC_VER)
|
|
OutputDebugStringA(txt);
|
|
#endif
|
|
|
|
len = strlen(txt);
|
|
|
|
#ifdef LOGMESSAGES
|
|
if (logstream)
|
|
{
|
|
size_t d = fwrite(txt, len, 1, logstream);
|
|
fflush(logstream);
|
|
(void)d;
|
|
}
|
|
#endif
|
|
|
|
#if defined (_WIN32)
|
|
#ifdef DEBUGFILE
|
|
if (debugfile != stderr)
|
|
#endif
|
|
{
|
|
HANDLE co = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
DWORD bytesWritten;
|
|
|
|
if (co == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
if (GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &bytesWritten))
|
|
{
|
|
static COORD coordNextWrite = {0,0};
|
|
LPVOID oldLines = NULL;
|
|
INT oldLength;
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
|
|
// Save the lines that we're going to obliterate.
|
|
GetConsoleScreenBufferInfo(co, &csbi);
|
|
oldLength = csbi.dwSize.X * (csbi.dwCursorPosition.Y - coordNextWrite.Y) + csbi.dwCursorPosition.X - coordNextWrite.X;
|
|
|
|
if (oldLength > 0)
|
|
{
|
|
LPVOID blank = malloc(oldLength);
|
|
if (!blank) return;
|
|
memset(blank, ' ', oldLength); // Blank out.
|
|
oldLines = malloc(oldLength*sizeof(TCHAR));
|
|
if (!oldLines)
|
|
{
|
|
free(blank);
|
|
return;
|
|
}
|
|
|
|
ReadConsoleOutputCharacter(co, oldLines, oldLength, coordNextWrite, &bytesWritten);
|
|
|
|
// Move to where we what to print - which is where we would've been,
|
|
// had console input not been in the way,
|
|
SetConsoleCursorPosition(co, coordNextWrite);
|
|
|
|
WriteConsoleA(co, blank, oldLength, &bytesWritten, NULL);
|
|
free(blank);
|
|
|
|
// And back to where we want to print again.
|
|
SetConsoleCursorPosition(co, coordNextWrite);
|
|
}
|
|
|
|
// Actually write the string now!
|
|
WriteConsoleA(co, txt, (DWORD)len, &bytesWritten, NULL);
|
|
|
|
// Next time, output where we left off.
|
|
GetConsoleScreenBufferInfo(co, &csbi);
|
|
coordNextWrite = csbi.dwCursorPosition;
|
|
|
|
// Restore what was overwritten.
|
|
if (oldLines && entering_con_command)
|
|
WriteConsole(co, oldLines, oldLength, &bytesWritten, NULL);
|
|
if (oldLines) free(oldLines);
|
|
}
|
|
else // Redirected to a file.
|
|
WriteFile(co, txt, (DWORD)len, &bytesWritten, NULL);
|
|
}
|
|
#else
|
|
#ifdef HAVE_TERMIOS
|
|
if (consolevent)
|
|
{
|
|
tty_Hide();
|
|
}
|
|
#endif
|
|
|
|
if (!framebuffer)
|
|
fprintf(stderr, "%s", txt);
|
|
#ifdef HAVE_TERMIOS
|
|
if (consolevent)
|
|
{
|
|
tty_Show();
|
|
}
|
|
#endif
|
|
|
|
// 2004-03-03 AJR Since not all messages end in newline, some were getting displayed late.
|
|
if (!framebuffer)
|
|
fflush(stderr);
|
|
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// I_GetKey
|
|
//
|
|
INT32 I_GetKey (void)
|
|
{
|
|
// Warning: I_GetKey empties the event queue till next keypress
|
|
event_t *ev;
|
|
INT32 rc = 0;
|
|
|
|
// return the first keypress from the event queue
|
|
for (; eventtail != eventhead; eventtail = (eventtail+1)&(MAXEVENTS-1))
|
|
{
|
|
ev = &events[eventtail];
|
|
if (ev->type == ev_keydown || ev->type == ev_console)
|
|
{
|
|
rc = ev->data1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// I_JoyScale
|
|
//
|
|
void I_JoyScale(void)
|
|
{
|
|
Joystick.bGamepadStyle = cv_joyscale.value==0;
|
|
JoyInfo.scale = Joystick.bGamepadStyle?1:cv_joyscale.value;
|
|
}
|
|
|
|
void I_JoyScale2(void)
|
|
{
|
|
Joystick2.bGamepadStyle = cv_joyscale2.value==0;
|
|
JoyInfo2.scale = Joystick2.bGamepadStyle?1:cv_joyscale2.value;
|
|
}
|
|
|
|
void I_JoyScale3(void)
|
|
{
|
|
Joystick3.bGamepadStyle = cv_joyscale3.value==0;
|
|
JoyInfo3.scale = Joystick3.bGamepadStyle?1:cv_joyscale3.value;
|
|
}
|
|
|
|
void I_JoyScale4(void)
|
|
{
|
|
Joystick4.bGamepadStyle = cv_joyscale4.value==0;
|
|
JoyInfo4.scale = Joystick4.bGamepadStyle?1:cv_joyscale4.value;
|
|
}
|
|
|
|
// Cheat to get the device index for a joystick handle
|
|
INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev)
|
|
{
|
|
INT32 i, count = SDL_NumJoysticks();
|
|
|
|
for (i = 0; dev && i < count; i++)
|
|
{
|
|
SDL_Joystick *test = SDL_JoystickOpen(i);
|
|
if (test && test == dev)
|
|
return i;
|
|
else if (JoyInfo.dev != test && JoyInfo2.dev != test && JoyInfo3.dev != test && JoyInfo4.dev != test)
|
|
SDL_JoystickClose(test);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Misleading function: updates device indices for all players BUT the one specified.
|
|
// Necessary for SDL_JOYDEVICEADDED events
|
|
void I_UpdateJoystickDeviceIndices(INT32 player)
|
|
{
|
|
if (player != 1) // This is a fucking mess.
|
|
{
|
|
//////////////////////////////
|
|
// update joystick 1's device index
|
|
//////////////////////////////
|
|
|
|
if (JoyInfo.dev)
|
|
cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
|
|
// is cv_usejoystick used?
|
|
else if (// don't check JoyInfo or cv_usejoystick; we're currently operating on those
|
|
atoi(cv_usejoystick.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick2.value
|
|
&& atoi(cv_usejoystick.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick3.value
|
|
&& atoi(cv_usejoystick.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick4.value)
|
|
cv_usejoystick.value = atoi(cv_usejoystick.string);
|
|
// is cv_usejoystick2 used?
|
|
else if ( // don't check JoyInfo or cv_usejoystick; we're currently operating on those
|
|
atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick2.value
|
|
&& atoi(cv_usejoystick2.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick3.value
|
|
&& atoi(cv_usejoystick2.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick4.value)
|
|
cv_usejoystick.value = atoi(cv_usejoystick2.string);
|
|
// is cv_usejoystick3 used?
|
|
else if (// don't check JoyInfo or cv_usejoystick; we're currently operating on those
|
|
atoi(cv_usejoystick3.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick2.value
|
|
&& atoi(cv_usejoystick3.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick3.value
|
|
&& atoi(cv_usejoystick3.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick4.value)
|
|
cv_usejoystick.value = atoi(cv_usejoystick3.string);
|
|
// is cv_usejoystick4 used?
|
|
else if (// don't check JoyInfo or cv_usejoystick; we're currently operating on those
|
|
atoi(cv_usejoystick4.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick2.value
|
|
&& atoi(cv_usejoystick4.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick3.value
|
|
&& atoi(cv_usejoystick4.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick4.value)
|
|
cv_usejoystick.value = atoi(cv_usejoystick4.string);
|
|
else // we tried...
|
|
cv_usejoystick.value = 0;
|
|
}
|
|
|
|
if (player != 2)
|
|
{
|
|
//////////////////////////////
|
|
// update joystick 2's device index
|
|
//////////////////////////////
|
|
|
|
if (JoyInfo2.dev)
|
|
cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
|
|
// is cv_usejoystick2 used?
|
|
else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick.value
|
|
// don't check JoyInfo2 or cv_usejoystick2; we're currently operating on those
|
|
&& atoi(cv_usejoystick2.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick3.value
|
|
&& atoi(cv_usejoystick2.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick4.value)
|
|
cv_usejoystick2.value = atoi(cv_usejoystick2.string);
|
|
// is cv_usejoystick used?
|
|
else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick.value
|
|
// don't check JoyInfo2 or cv_usejoystick2; we're currently operating on those
|
|
&& atoi(cv_usejoystick.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick3.value
|
|
&& atoi(cv_usejoystick.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick4.value)
|
|
cv_usejoystick2.value = atoi(cv_usejoystick.string);
|
|
// is cv_usejoystick3 used?
|
|
else if (atoi(cv_usejoystick3.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick.value
|
|
// don't check JoyInfo2 or cv_usejoystick2; we're currently operating on those
|
|
&& atoi(cv_usejoystick3.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick3.value
|
|
&& atoi(cv_usejoystick3.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick4.value)
|
|
cv_usejoystick2.value = atoi(cv_usejoystick3.string);
|
|
// is cv_usejoystick4 used?
|
|
else if (atoi(cv_usejoystick4.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick.value
|
|
// don't check JoyInfo2 or cv_usejoystick2; we're currently operating on those
|
|
&& atoi(cv_usejoystick4.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick3.value
|
|
&& atoi(cv_usejoystick4.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick4.value)
|
|
cv_usejoystick2.value = atoi(cv_usejoystick4.string);
|
|
else // we tried...
|
|
cv_usejoystick2.value = 0;
|
|
}
|
|
|
|
if (player != 3)
|
|
{
|
|
//////////////////////////////
|
|
// update joystick 3's device index
|
|
//////////////////////////////
|
|
|
|
if (JoyInfo3.dev)
|
|
cv_usejoystick3.value = I_GetJoystickDeviceIndex(JoyInfo3.dev) + 1;
|
|
// is cv_usejoystick3 used?
|
|
else if (atoi(cv_usejoystick3.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick.value
|
|
&& atoi(cv_usejoystick3.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick2.value
|
|
// don't check JoyInfo3 or cv_usejoystick3; we're currently operating on those
|
|
&& atoi(cv_usejoystick3.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick4.value)
|
|
cv_usejoystick3.value = atoi(cv_usejoystick3.string);
|
|
// is cv_usejoystick used?
|
|
else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick.value
|
|
&& atoi(cv_usejoystick.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick2.value
|
|
// don't check JoyInfo3 or cv_usejoystick3; we're currently operating on those
|
|
&& atoi(cv_usejoystick.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick4.value)
|
|
cv_usejoystick3.value = atoi(cv_usejoystick.string);
|
|
// is cv_usejoystick2 used?
|
|
else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick.value
|
|
&& atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick2.value
|
|
// don't check JoyInfo3 or cv_usejoystick3; we're currently operating on those
|
|
&& atoi(cv_usejoystick2.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick4.value)
|
|
cv_usejoystick3.value = atoi(cv_usejoystick2.string);
|
|
// is cv_usejoystick4 used?
|
|
else if (atoi(cv_usejoystick4.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick.value
|
|
&& atoi(cv_usejoystick4.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick2.value
|
|
// don't check JoyInfo3 or cv_usejoystick3; we're currently operating on those
|
|
&& atoi(cv_usejoystick4.string) != JoyInfo4.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick4.value)
|
|
cv_usejoystick3.value = atoi(cv_usejoystick4.string);
|
|
else // we tried...
|
|
cv_usejoystick3.value = 0;
|
|
}
|
|
|
|
if (player != 4)
|
|
{
|
|
//////////////////////////////
|
|
// update joystick 4's device index
|
|
//////////////////////////////
|
|
|
|
if (JoyInfo4.dev)
|
|
cv_usejoystick4.value = I_GetJoystickDeviceIndex(JoyInfo4.dev) + 1;
|
|
// is cv_usejoystick4 used?
|
|
else if (atoi(cv_usejoystick4.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick.value
|
|
&& atoi(cv_usejoystick4.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick2.value
|
|
&& atoi(cv_usejoystick4.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick4.string) != cv_usejoystick3.value)
|
|
// don't check JoyInfo4 or cv_usejoystick4; we're currently operating on those
|
|
cv_usejoystick4.value = atoi(cv_usejoystick4.string);
|
|
// is cv_usejoystick used?
|
|
else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick.value
|
|
&& atoi(cv_usejoystick.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick2.value
|
|
&& atoi(cv_usejoystick.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick.string) != cv_usejoystick3.value)
|
|
// don't check JoyInfo4 or cv_usejoystick4; we're currently operating on those
|
|
cv_usejoystick4.value = atoi(cv_usejoystick.string);
|
|
// is cv_usejoystick2 used?
|
|
else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick.value
|
|
&& atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick2.value
|
|
&& atoi(cv_usejoystick2.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick2.string) != cv_usejoystick3.value)
|
|
// don't check JoyInfo4 or cv_usejoystick4; we're currently operating on those
|
|
cv_usejoystick4.value = atoi(cv_usejoystick2.string);
|
|
// is cv_usejoystick3 used?
|
|
else if (atoi(cv_usejoystick3.string) != JoyInfo.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick.value
|
|
&& atoi(cv_usejoystick3.string) != JoyInfo2.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick2.value
|
|
&& atoi(cv_usejoystick3.string) != JoyInfo3.oldjoy
|
|
&& atoi(cv_usejoystick3.string) != cv_usejoystick3.value)
|
|
// don't check JoyInfo4 or cv_usejoystick4; we're currently operating on those
|
|
cv_usejoystick4.value = atoi(cv_usejoystick3.string);
|
|
else // we tried...
|
|
cv_usejoystick4.value = 0;
|
|
}
|
|
}
|
|
|
|
/** \brief Joystick 1 buttons states
|
|
*/
|
|
static UINT64 lastjoybuttons = 0;
|
|
|
|
/** \brief Joystick 1 hats state
|
|
*/
|
|
static UINT64 lastjoyhats = 0;
|
|
|
|
/** \brief Shuts down joystick 1
|
|
|
|
|
|
\return void
|
|
|
|
|
|
*/
|
|
void I_ShutdownJoystick(void)
|
|
{
|
|
INT32 i;
|
|
event_t event;
|
|
event.type=ev_keyup;
|
|
event.data2 = 0;
|
|
event.data3 = 0;
|
|
|
|
lastjoybuttons = lastjoyhats = 0;
|
|
|
|
// emulate the up of all joystick buttons
|
|
for (i=0;i<JOYBUTTONS;i++)
|
|
{
|
|
event.data1=KEY_JOY1+i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
// emulate the up of all joystick hats
|
|
for (i=0;i<JOYHATS*4;i++)
|
|
{
|
|
event.data1=KEY_HAT1+i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
// reset joystick position
|
|
event.type = ev_joystick;
|
|
for (i=0;i<JOYAXISSET; i++)
|
|
{
|
|
event.data1 = i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
joystick_started = 0;
|
|
JoyReset(&JoyInfo);
|
|
|
|
// don't shut down the subsystem here, because hotplugging
|
|
}
|
|
|
|
void I_GetJoystickEvents(void)
|
|
{
|
|
static event_t event = {0,0,0,0};
|
|
INT32 i = 0;
|
|
UINT64 joyhats = 0;
|
|
#if 0
|
|
UINT64 joybuttons = 0;
|
|
Sint16 axisx, axisy;
|
|
#endif
|
|
|
|
if (!joystick_started) return;
|
|
|
|
if (!JoyInfo.dev) //I_ShutdownJoystick();
|
|
return;
|
|
|
|
#if 0
|
|
//faB: look for as much buttons as g_input code supports,
|
|
// we don't use the others
|
|
for (i = JoyInfo.buttons - 1; i >= 0; i--)
|
|
{
|
|
joybuttons <<= 1;
|
|
if (SDL_JoystickGetButton(JoyInfo.dev,i))
|
|
joybuttons |= 1;
|
|
}
|
|
|
|
if (joybuttons != lastjoybuttons)
|
|
{
|
|
INT64 j = 1; // keep only bits that changed since last time
|
|
INT64 newbuttons = joybuttons ^ lastjoybuttons;
|
|
lastjoybuttons = joybuttons;
|
|
|
|
for (i = 0; i < JOYBUTTONS; i++, j <<= 1)
|
|
{
|
|
if (newbuttons & j) // button changed state?
|
|
{
|
|
if (joybuttons & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_JOY1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (i = JoyInfo.hats - 1; i >= 0; i--)
|
|
{
|
|
Uint8 hat = SDL_JoystickGetHat(JoyInfo.dev, i);
|
|
|
|
if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i);
|
|
if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i);
|
|
if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i);
|
|
if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i);
|
|
}
|
|
|
|
if (joyhats != lastjoyhats)
|
|
{
|
|
INT64 j = 1; // keep only bits that changed since last time
|
|
INT64 newhats = joyhats ^ lastjoyhats;
|
|
lastjoyhats = joyhats;
|
|
|
|
for (i = 0; i < JOYHATS*4; i++, j <<= 1)
|
|
{
|
|
if (newhats & j) // hat changed state?
|
|
{
|
|
if (joyhats & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_HAT1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// send joystick axis positions
|
|
event.type = ev_joystick;
|
|
|
|
for (i = JOYAXISSET - 1; i >= 0; i--)
|
|
{
|
|
event.data1 = i;
|
|
if (i*2 + 1 <= JoyInfo.axises)
|
|
axisx = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 0);
|
|
else axisx = 0;
|
|
if (i*2 + 2 <= JoyInfo.axises)
|
|
axisy = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 1);
|
|
else axisy = 0;
|
|
|
|
|
|
// -32768 to 32767
|
|
axisx = axisx/32;
|
|
axisy = axisy/32;
|
|
|
|
|
|
if (Joystick.bGamepadStyle)
|
|
{
|
|
// gamepad control type, on or off, live or die
|
|
if (axisx < -(JOYAXISRANGE/2))
|
|
event.data2 = -1;
|
|
else if (axisx > (JOYAXISRANGE/2))
|
|
event.data2 = 1;
|
|
else event.data2 = 0;
|
|
if (axisy < -(JOYAXISRANGE/2))
|
|
event.data3 = -1;
|
|
else if (axisy > (JOYAXISRANGE/2))
|
|
event.data3 = 1;
|
|
else event.data3 = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
axisx = JoyInfo.scale?((axisx/JoyInfo.scale)*JoyInfo.scale):axisx;
|
|
axisy = JoyInfo.scale?((axisy/JoyInfo.scale)*JoyInfo.scale):axisy;
|
|
|
|
#ifdef SDL_JDEADZONE
|
|
if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0;
|
|
if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0;
|
|
#endif
|
|
|
|
// analog control style , just send the raw data
|
|
event.data2 = axisx; // x axis
|
|
event.data3 = axisy; // y axis
|
|
}
|
|
D_PostEvent(&event);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/** \brief Open joystick handle
|
|
|
|
\param fname name of joystick
|
|
|
|
\return axises
|
|
|
|
|
|
*/
|
|
static int joy_open(int joyindex)
|
|
{
|
|
SDL_Joystick *newdev = NULL;
|
|
int num_joy = 0;
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
|
|
{
|
|
CONS_Printf(M_GetText("Joystick subsystem not started\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (joyindex <= 0)
|
|
return -1;
|
|
|
|
num_joy = SDL_NumJoysticks();
|
|
|
|
if (num_joy == 0)
|
|
{
|
|
CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
|
|
return -1;
|
|
}
|
|
|
|
newdev = SDL_JoystickOpen(joyindex-1);
|
|
|
|
// Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
|
|
// This indexing is SDL's responsibility and there's not much we can do about it.
|
|
//
|
|
// Example:
|
|
// 1. Plug Controller A -> Index 0 opened
|
|
// 2. Plug Controller B -> Index 1 opened
|
|
// 3. Unplug Controller A -> Index 0 closed, Index 1 active
|
|
// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
|
|
// 5. Plug Controller B -> Index 0 opened
|
|
// 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
|
|
if (JoyInfo.dev)
|
|
{
|
|
if (JoyInfo.dev == newdev // same device, nothing to do
|
|
|| (newdev == NULL && SDL_JoystickGetAttached(JoyInfo.dev))) // we failed, but already have a working device
|
|
return JoyInfo.axises;
|
|
// Else, we're changing devices, so send neutral joy events
|
|
CONS_Debug(DBG_GAMELOGIC, "Joystick1 device is changing; resetting events...\n");
|
|
I_ShutdownJoystick();
|
|
}
|
|
|
|
JoyInfo.dev = newdev;
|
|
|
|
if (JoyInfo.dev == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: Couldn't open device - %s\n"), SDL_GetError());
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: %s\n"), SDL_JoystickName(JoyInfo.dev));
|
|
JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev);
|
|
if (JoyInfo.axises > JOYAXISSET*2)
|
|
JoyInfo.axises = JOYAXISSET*2;
|
|
/* if (joyaxes<2)
|
|
{
|
|
I_OutputMsg("Not enought axes?\n");
|
|
return 0;
|
|
}*/
|
|
|
|
JoyInfo.buttons = SDL_JoystickNumButtons(JoyInfo.dev);
|
|
if (JoyInfo.buttons > JOYBUTTONS)
|
|
JoyInfo.buttons = JOYBUTTONS;
|
|
|
|
JoyInfo.hats = SDL_JoystickNumHats(JoyInfo.dev);
|
|
if (JoyInfo.hats > JOYHATS)
|
|
JoyInfo.hats = JOYHATS;
|
|
|
|
JoyInfo.balls = SDL_JoystickNumBalls(JoyInfo.dev);
|
|
|
|
//Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo.dev), "pad");
|
|
|
|
return JoyInfo.axises;
|
|
}
|
|
}
|
|
|
|
//Joystick2
|
|
|
|
/** \brief Joystick 2 buttons states
|
|
*/
|
|
static UINT64 lastjoy2buttons = 0;
|
|
|
|
/** \brief Joystick 2 hats state
|
|
*/
|
|
static UINT64 lastjoy2hats = 0;
|
|
|
|
/** \brief Shuts down joystick 2
|
|
|
|
|
|
\return void
|
|
*/
|
|
void I_ShutdownJoystick2(void)
|
|
{
|
|
INT32 i;
|
|
event_t event;
|
|
event.type = ev_keyup;
|
|
event.data2 = 0;
|
|
event.data3 = 0;
|
|
|
|
lastjoy2buttons = lastjoy2hats = 0;
|
|
|
|
// emulate the up of all joystick buttons
|
|
for (i = 0; i < JOYBUTTONS; i++)
|
|
{
|
|
event.data1 = KEY_2JOY1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
// emulate the up of all joystick hats
|
|
for (i = 0; i < JOYHATS*4; i++)
|
|
{
|
|
event.data1 = KEY_2HAT1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
// reset joystick position
|
|
event.type = ev_joystick2;
|
|
for (i = 0; i < JOYAXISSET; i++)
|
|
{
|
|
event.data1 = i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
joystick2_started = 0;
|
|
JoyReset(&JoyInfo2);
|
|
|
|
// don't shut down the subsystem here, because hotplugging
|
|
}
|
|
|
|
void I_GetJoystick2Events(void)
|
|
{
|
|
static event_t event = {0,0,0,0};
|
|
INT32 i = 0;
|
|
UINT64 joyhats = 0;
|
|
#if 0
|
|
INT64 joybuttons = 0;
|
|
INT32 axisx, axisy;
|
|
#endif
|
|
|
|
if (!joystick2_started)
|
|
return;
|
|
|
|
if (!JoyInfo2.dev) //I_ShutdownJoystick2();
|
|
return;
|
|
|
|
|
|
#if 0
|
|
//faB: look for as much buttons as g_input code supports,
|
|
// we don't use the others
|
|
for (i = JoyInfo2.buttons - 1; i >= 0; i--)
|
|
{
|
|
joybuttons <<= 1;
|
|
if (SDL_JoystickGetButton(JoyInfo2.dev,i))
|
|
joybuttons |= 1;
|
|
}
|
|
|
|
if (joybuttons != lastjoy2buttons)
|
|
{
|
|
INT64 j = 1; // keep only bits that changed since last time
|
|
INT64 newbuttons = joybuttons ^ lastjoy2buttons;
|
|
lastjoy2buttons = joybuttons;
|
|
|
|
for (i = 0; i < JOYBUTTONS; i++, j <<= 1)
|
|
{
|
|
if (newbuttons & j) // button changed state?
|
|
{
|
|
if (joybuttons & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_2JOY1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (i = JoyInfo2.hats - 1; i >= 0; i--)
|
|
{
|
|
Uint8 hat = SDL_JoystickGetHat(JoyInfo2.dev, i);
|
|
|
|
if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i);
|
|
if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i);
|
|
if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i);
|
|
if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i);
|
|
}
|
|
|
|
if (joyhats != lastjoy2hats)
|
|
{
|
|
INT64 j = 1; // keep only bits that changed since last time
|
|
INT64 newhats = joyhats ^ lastjoy2hats;
|
|
lastjoy2hats = joyhats;
|
|
|
|
for (i = 0; i < JOYHATS*4; i++, j <<= 1)
|
|
{
|
|
if (newhats & j) // hat changed state?
|
|
{
|
|
if (joyhats & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_2HAT1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// send joystick axis positions
|
|
event.type = ev_joystick2;
|
|
|
|
for (i = JOYAXISSET - 1; i >= 0; i--)
|
|
{
|
|
event.data1 = i;
|
|
if (i*2 + 1 <= JoyInfo2.axises)
|
|
axisx = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 0);
|
|
else axisx = 0;
|
|
if (i*2 + 2 <= JoyInfo2.axises)
|
|
axisy = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 1);
|
|
else axisy = 0;
|
|
|
|
// -32768 to 32767
|
|
axisx = axisx/32;
|
|
axisy = axisy/32;
|
|
|
|
if (Joystick2.bGamepadStyle)
|
|
{
|
|
// gamepad control type, on or off, live or die
|
|
if (axisx < -(JOYAXISRANGE/2))
|
|
event.data2 = -1;
|
|
else if (axisx > (JOYAXISRANGE/2))
|
|
event.data2 = 1;
|
|
else
|
|
event.data2 = 0;
|
|
if (axisy < -(JOYAXISRANGE/2))
|
|
event.data3 = -1;
|
|
else if (axisy > (JOYAXISRANGE/2))
|
|
event.data3 = 1;
|
|
else
|
|
event.data3 = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
axisx = JoyInfo2.scale?((axisx/JoyInfo2.scale)*JoyInfo2.scale):axisx;
|
|
axisy = JoyInfo2.scale?((axisy/JoyInfo2.scale)*JoyInfo2.scale):axisy;
|
|
|
|
#ifdef SDL_JDEADZONE
|
|
if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0;
|
|
if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0;
|
|
#endif
|
|
|
|
// analog control style , just send the raw data
|
|
event.data2 = axisx; // x axis
|
|
event.data3 = axisy; // y axis
|
|
}
|
|
D_PostEvent(&event);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/** \brief Open joystick handle
|
|
|
|
\param fname name of joystick
|
|
|
|
\return axises
|
|
|
|
|
|
*/
|
|
static int joy_open2(int joyindex)
|
|
{
|
|
SDL_Joystick *newdev = NULL;
|
|
int num_joy = 0;
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
|
|
{
|
|
CONS_Printf(M_GetText("Joystick subsystem not started\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (joyindex <= 0)
|
|
return -1;
|
|
|
|
num_joy = SDL_NumJoysticks();
|
|
|
|
if (num_joy == 0)
|
|
{
|
|
CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
|
|
return -1;
|
|
}
|
|
|
|
newdev = SDL_JoystickOpen(joyindex-1);
|
|
|
|
// Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
|
|
// This indexing is SDL's responsibility and there's not much we can do about it.
|
|
//
|
|
// Example:
|
|
// 1. Plug Controller A -> Index 0 opened
|
|
// 2. Plug Controller B -> Index 1 opened
|
|
// 3. Unplug Controller A -> Index 0 closed, Index 1 active
|
|
// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
|
|
// 5. Plug Controller B -> Index 0 opened
|
|
// 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
|
|
if (JoyInfo2.dev)
|
|
{
|
|
if (JoyInfo2.dev == newdev // same device, nothing to do
|
|
|| (newdev == NULL && SDL_JoystickGetAttached(JoyInfo2.dev))) // we failed, but already have a working device
|
|
return JoyInfo.axises;
|
|
// Else, we're changing devices, so send neutral joy events
|
|
CONS_Debug(DBG_GAMELOGIC, "Joystick2 device is changing; resetting events...\n");
|
|
I_ShutdownJoystick2();
|
|
}
|
|
|
|
JoyInfo2.dev = newdev;
|
|
|
|
if (JoyInfo2.dev == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: couldn't open device - %s\n"), SDL_GetError());
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev));
|
|
JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev);
|
|
if (JoyInfo2.axises > JOYAXISSET*2)
|
|
JoyInfo2.axises = JOYAXISSET*2;
|
|
/* if (joyaxes<2)
|
|
{
|
|
I_OutputMsg("Not enought axes?\n");
|
|
return 0;
|
|
}*/
|
|
|
|
JoyInfo2.buttons = SDL_JoystickNumButtons(JoyInfo2.dev);
|
|
if (JoyInfo2.buttons > JOYBUTTONS)
|
|
JoyInfo2.buttons = JOYBUTTONS;
|
|
|
|
JoyInfo2.hats = SDL_JoystickNumHats(JoyInfo2.dev);
|
|
if (JoyInfo2.hats > JOYHATS)
|
|
JoyInfo2.hats = JOYHATS;
|
|
|
|
JoyInfo2.balls = SDL_JoystickNumBalls(JoyInfo2.dev);
|
|
|
|
//Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo2.dev), "pad");
|
|
|
|
return JoyInfo2.axises;
|
|
}
|
|
}
|
|
|
|
//Joystick3
|
|
|
|
/** \brief Joystick 3 buttons states
|
|
*/
|
|
static UINT64 lastjoy3buttons = 0;
|
|
|
|
/** \brief Joystick 3 hats state
|
|
*/
|
|
static UINT64 lastjoy3hats = 0;
|
|
|
|
/** \brief Shuts down joystick 3
|
|
|
|
|
|
\return void
|
|
*/
|
|
void I_ShutdownJoystick3(void)
|
|
{
|
|
INT32 i;
|
|
event_t event;
|
|
event.type = ev_keyup;
|
|
event.data2 = 0;
|
|
event.data3 = 0;
|
|
|
|
lastjoy3buttons = lastjoy3hats = 0;
|
|
|
|
// emulate the up of all joystick buttons
|
|
for (i = 0; i < JOYBUTTONS; i++)
|
|
{
|
|
event.data1 = KEY_3JOY1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
// emulate the up of all joystick hats
|
|
for (i = 0; i < JOYHATS*4; i++)
|
|
{
|
|
event.data1 = KEY_3HAT1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
// reset joystick position
|
|
event.type = ev_joystick3;
|
|
for (i = 0; i < JOYAXISSET; i++)
|
|
{
|
|
event.data1 = i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
joystick3_started = 0;
|
|
JoyReset(&JoyInfo3);
|
|
|
|
// don't shutdown the subsystem here, because hotplugging
|
|
}
|
|
|
|
void I_GetJoystick3Events(void)
|
|
{
|
|
static event_t event = {0,0,0,0};
|
|
INT32 i = 0;
|
|
UINT64 joyhats = 0;
|
|
#if 0
|
|
INT64 joybuttons = 0;
|
|
#endif
|
|
INT32 axisx, axisy;
|
|
|
|
if (!joystick3_started)
|
|
return;
|
|
|
|
if (!JoyInfo3.dev) //I_ShutdownJoystick3();
|
|
return;
|
|
|
|
|
|
#if 0
|
|
//faB: look for as much buttons as g_input code supports,
|
|
// we don't use the others
|
|
for (i = JoyInfo3.buttons - 1; i >= 0; i--)
|
|
{
|
|
joybuttons <<= 1;
|
|
if (SDL_JoystickGetButton(JoyInfo3.dev,i))
|
|
joybuttons |= 1;
|
|
}
|
|
|
|
if (joybuttons != lastjoy3buttons)
|
|
{
|
|
INT64 j = 1; // keep only bits that changed since last time
|
|
INT64 newbuttons = joybuttons ^ lastjoy3buttons;
|
|
lastjoy3buttons = joybuttons;
|
|
|
|
for (i = 0; i < JOYBUTTONS; i++, j <<= 1)
|
|
{
|
|
if (newbuttons & j) // button changed state?
|
|
{
|
|
if (joybuttons & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_3JOY1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (i = JoyInfo3.hats - 1; i >= 0; i--)
|
|
{
|
|
Uint8 hat = SDL_JoystickGetHat(JoyInfo3.dev, i);
|
|
|
|
if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i);
|
|
if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i);
|
|
if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i);
|
|
if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i);
|
|
}
|
|
|
|
if (joyhats != lastjoy3hats)
|
|
{
|
|
INT64 j = 1; // keep only bits that changed since last time
|
|
INT64 newhats = joyhats ^ lastjoy3hats;
|
|
lastjoy3hats = joyhats;
|
|
|
|
for (i = 0; i < JOYHATS*4; i++, j <<= 1)
|
|
{
|
|
if (newhats & j) // hat changed state?
|
|
{
|
|
if (joyhats & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_3HAT1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
|
|
// send joystick axis positions
|
|
event.type = ev_joystick3;
|
|
|
|
for (i = JOYAXISSET - 1; i >= 0; i--)
|
|
{
|
|
event.data1 = i;
|
|
if (i*2 + 1 <= JoyInfo3.axises)
|
|
axisx = SDL_JoystickGetAxis(JoyInfo3.dev, i*2 + 0);
|
|
else axisx = 0;
|
|
if (i*2 + 2 <= JoyInfo3.axises)
|
|
axisy = SDL_JoystickGetAxis(JoyInfo3.dev, i*2 + 1);
|
|
else axisy = 0;
|
|
|
|
// -32768 to 32767
|
|
axisx = axisx/32;
|
|
axisy = axisy/32;
|
|
|
|
if (Joystick3.bGamepadStyle)
|
|
{
|
|
// gamepad control type, on or off, live or die
|
|
if (axisx < -(JOYAXISRANGE/2))
|
|
event.data2 = -1;
|
|
else if (axisx > (JOYAXISRANGE/2))
|
|
event.data2 = 1;
|
|
else
|
|
event.data2 = 0;
|
|
if (axisy < -(JOYAXISRANGE/2))
|
|
event.data3 = -1;
|
|
else if (axisy > (JOYAXISRANGE/2))
|
|
event.data3 = 1;
|
|
else
|
|
event.data3 = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
axisx = JoyInfo3.scale?((axisx/JoyInfo3.scale)*JoyInfo3.scale):axisx;
|
|
axisy = JoyInfo3.scale?((axisy/JoyInfo3.scale)*JoyInfo3.scale):axisy;
|
|
|
|
#ifdef SDL_JDEADZONE
|
|
if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0;
|
|
if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0;
|
|
#endif
|
|
|
|
// analog control style , just send the raw data
|
|
event.data2 = axisx; // x axis
|
|
event.data3 = axisy; // y axis
|
|
}
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
}
|
|
|
|
/** \brief Open joystick handle
|
|
|
|
\param fname name of joystick
|
|
|
|
\return axises
|
|
|
|
|
|
*/
|
|
static int joy_open3(int joyindex)
|
|
{
|
|
SDL_Joystick *newdev = NULL;
|
|
int num_joy = 0;
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
|
|
{
|
|
CONS_Printf(M_GetText("Joystick subsystem not started\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (joyindex <= 0)
|
|
return -1;
|
|
|
|
num_joy = SDL_NumJoysticks();
|
|
|
|
if (num_joy == 0)
|
|
{
|
|
CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
|
|
return -1;
|
|
}
|
|
|
|
newdev = SDL_JoystickOpen(joyindex - 1);
|
|
|
|
// Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
|
|
// This indexing is SDL's responsibility and there's not much we can do about it.
|
|
//
|
|
// Example:
|
|
// 1. Plug Controller A -> Index 0 opened
|
|
// 2. Plug Controller B -> Index 1 opened
|
|
// 3. Unplug Controller A -> Index 0 closed, Index 1 active
|
|
// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
|
|
// 5. Plug Controller B -> Index 0 opened
|
|
// 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
|
|
if (JoyInfo3.dev)
|
|
{
|
|
if (JoyInfo3.dev == newdev // same device, nothing to do
|
|
|| (newdev == NULL && SDL_JoystickGetAttached(JoyInfo3.dev))) // we failed, but already have a working device
|
|
return JoyInfo.axises;
|
|
// Else, we're changing devices, so send neutral joy events
|
|
CONS_Debug(DBG_GAMELOGIC, "Joystick3 device is changing; resetting events...\n");
|
|
I_ShutdownJoystick3();
|
|
}
|
|
|
|
JoyInfo3.dev = newdev;
|
|
|
|
if (JoyInfo3.dev == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick3: couldn't open device - %s\n"), SDL_GetError());
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick3: %s\n"), SDL_JoystickName(JoyInfo3.dev));
|
|
JoyInfo3.axises = SDL_JoystickNumAxes(JoyInfo3.dev);
|
|
if (JoyInfo3.axises > JOYAXISSET * 2)
|
|
JoyInfo3.axises = JOYAXISSET * 2;
|
|
/* if (joyaxes<2)
|
|
{
|
|
I_OutputMsg("Not enought axes?\n");
|
|
return 0;
|
|
}*/
|
|
|
|
JoyInfo3.buttons = SDL_JoystickNumButtons(JoyInfo3.dev);
|
|
if (JoyInfo3.buttons > JOYBUTTONS)
|
|
JoyInfo3.buttons = JOYBUTTONS;
|
|
|
|
JoyInfo3.hats = SDL_JoystickNumHats(JoyInfo3.dev);
|
|
if (JoyInfo3.hats > JOYHATS)
|
|
JoyInfo3.hats = JOYHATS;
|
|
|
|
JoyInfo3.balls = SDL_JoystickNumBalls(JoyInfo3.dev);
|
|
|
|
//Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo3.dev), "pad");
|
|
|
|
return JoyInfo3.axises;
|
|
}
|
|
}
|
|
|
|
//Joystick4
|
|
|
|
/** \brief Joystick 4 buttons states
|
|
*/
|
|
static UINT64 lastjoy4buttons = 0;
|
|
|
|
/** \brief Joystick 4 hats state
|
|
*/
|
|
static UINT64 lastjoy4hats = 0;
|
|
|
|
/** \brief Shuts down joystick 4
|
|
|
|
|
|
\return void
|
|
*/
|
|
void I_ShutdownJoystick4(void)
|
|
{
|
|
INT32 i;
|
|
event_t event;
|
|
event.type = ev_keyup;
|
|
event.data2 = 0;
|
|
event.data3 = 0;
|
|
|
|
lastjoy4buttons = lastjoy4hats = 0;
|
|
|
|
// emulate the up of all joystick buttons
|
|
for (i = 0; i < JOYBUTTONS; i++)
|
|
{
|
|
event.data1 = KEY_4JOY1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
// emulate the up of all joystick hats
|
|
for (i = 0; i < JOYHATS*4; i++)
|
|
{
|
|
event.data1 = KEY_4HAT1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
// reset joystick position
|
|
event.type = ev_joystick4;
|
|
for (i = 0; i < JOYAXISSET; i++)
|
|
{
|
|
event.data1 = i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
joystick4_started = 0;
|
|
JoyReset(&JoyInfo4);
|
|
|
|
// don't shutdown the subsystem here, because hotplugging
|
|
}
|
|
|
|
void I_GetJoystick4Events(void)
|
|
{
|
|
static event_t event = {0,0,0,0};
|
|
INT32 i = 0;
|
|
UINT64 joyhats = 0;
|
|
#if 0
|
|
INT64 joybuttons = 0;
|
|
#endif
|
|
INT32 axisx, axisy;
|
|
|
|
if (!joystick4_started)
|
|
return;
|
|
|
|
if (!JoyInfo4.dev) //I_ShutdownJoystick4();
|
|
return;
|
|
|
|
|
|
#if 0
|
|
//faB: look for as much buttons as g_input code supports,
|
|
// we don't use the others
|
|
for (i = JoyInfo4.buttons - 1; i >= 0; i--)
|
|
{
|
|
joybuttons <<= 1;
|
|
if (SDL_JoystickGetButton(JoyInfo4.dev,i))
|
|
joybuttons |= 1;
|
|
}
|
|
|
|
if (joybuttons != lastjoy4buttons)
|
|
{
|
|
INT64 j = 1; // keep only bits that changed since last time
|
|
INT64 newbuttons = joybuttons ^ lastjoy4buttons;
|
|
lastjoy4buttons = joybuttons;
|
|
|
|
for (i = 0; i < JOYBUTTONS; i++, j <<= 1)
|
|
{
|
|
if (newbuttons & j) // button changed state?
|
|
{
|
|
if (joybuttons & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_4JOY1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (i = JoyInfo4.hats - 1; i >= 0; i--)
|
|
{
|
|
Uint8 hat = SDL_JoystickGetHat(JoyInfo4.dev, i);
|
|
|
|
if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i);
|
|
if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i);
|
|
if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i);
|
|
if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i);
|
|
}
|
|
|
|
if (joyhats != lastjoy4hats)
|
|
{
|
|
INT64 j = 1; // keep only bits that changed since last time
|
|
INT64 newhats = joyhats ^ lastjoy4hats;
|
|
lastjoy4hats = joyhats;
|
|
|
|
for (i = 0; i < JOYHATS*4; i++, j <<= 1)
|
|
{
|
|
if (newhats & j) // hat changed state?
|
|
{
|
|
if (joyhats & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_4HAT1 + i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
|
|
// send joystick axis positions
|
|
event.type = ev_joystick4;
|
|
|
|
for (i = JOYAXISSET - 1; i >= 0; i--)
|
|
{
|
|
event.data1 = i;
|
|
if (i*2 + 1 <= JoyInfo4.axises)
|
|
axisx = SDL_JoystickGetAxis(JoyInfo4.dev, i*2 + 0);
|
|
else axisx = 0;
|
|
if (i*2 + 2 <= JoyInfo4.axises)
|
|
axisy = SDL_JoystickGetAxis(JoyInfo4.dev, i*2 + 1);
|
|
else axisy = 0;
|
|
|
|
// -32768 to 32767
|
|
axisx = axisx/32;
|
|
axisy = axisy/32;
|
|
|
|
if (Joystick4.bGamepadStyle)
|
|
{
|
|
// gamepad control type, on or off, live or die
|
|
if (axisx < -(JOYAXISRANGE/2))
|
|
event.data2 = -1;
|
|
else if (axisx > (JOYAXISRANGE/2))
|
|
event.data2 = 1;
|
|
else
|
|
event.data2 = 0;
|
|
if (axisy < -(JOYAXISRANGE/2))
|
|
event.data3 = -1;
|
|
else if (axisy > (JOYAXISRANGE/2))
|
|
event.data3 = 1;
|
|
else
|
|
event.data3 = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
axisx = JoyInfo4.scale?((axisx/JoyInfo4.scale)*JoyInfo4.scale):axisx;
|
|
axisy = JoyInfo4.scale?((axisy/JoyInfo4.scale)*JoyInfo4.scale):axisy;
|
|
|
|
#ifdef SDL_JDEADZONE
|
|
if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0;
|
|
if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0;
|
|
#endif
|
|
|
|
// analog control style , just send the raw data
|
|
event.data2 = axisx; // x axis
|
|
event.data3 = axisy; // y axis
|
|
}
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
}
|
|
|
|
/** \brief Open joystick handle
|
|
|
|
\param fname name of joystick
|
|
|
|
\return axises
|
|
|
|
|
|
*/
|
|
static int joy_open4(int joyindex)
|
|
{
|
|
SDL_Joystick *newdev = NULL;
|
|
int num_joy = 0;
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
|
|
{
|
|
CONS_Printf(M_GetText("Joystick subsystem not started\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (joyindex <= 0)
|
|
return -1;
|
|
|
|
num_joy = SDL_NumJoysticks();
|
|
|
|
if (num_joy == 0)
|
|
{
|
|
CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
|
|
return -1;
|
|
}
|
|
|
|
newdev = SDL_JoystickOpen(joyindex - 1);
|
|
|
|
// Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
|
|
// This indexing is SDL's responsibility and there's not much we can do about it.
|
|
//
|
|
// Example:
|
|
// 1. Plug Controller A -> Index 0 opened
|
|
// 2. Plug Controller B -> Index 1 opened
|
|
// 3. Unplug Controller A -> Index 0 closed, Index 1 active
|
|
// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
|
|
// 5. Plug Controller B -> Index 0 opened
|
|
// 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
|
|
if (JoyInfo4.dev)
|
|
{
|
|
if (JoyInfo4.dev == newdev // same device, nothing to do
|
|
|| (newdev == NULL && SDL_JoystickGetAttached(JoyInfo4.dev))) // we failed, but already have a working device
|
|
return JoyInfo.axises;
|
|
// Else, we're changing devices, so send neutral joy events
|
|
CONS_Debug(DBG_GAMELOGIC, "Joystick4 device is changing; resetting events...\n");
|
|
I_ShutdownJoystick4();
|
|
}
|
|
|
|
JoyInfo4.dev = newdev;
|
|
|
|
if (JoyInfo4.dev == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick4: couldn't open device - %s\n"), SDL_GetError());
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick4: %s\n"), SDL_JoystickName(JoyInfo4.dev));
|
|
JoyInfo4.axises = SDL_JoystickNumAxes(JoyInfo4.dev);
|
|
if (JoyInfo4.axises > JOYAXISSET * 2)
|
|
JoyInfo4.axises = JOYAXISSET * 2;
|
|
/* if (joyaxes<2)
|
|
{
|
|
I_OutputMsg("Not enought axes?\n");
|
|
return 0;
|
|
}*/
|
|
|
|
JoyInfo4.buttons = SDL_JoystickNumButtons(JoyInfo4.dev);
|
|
if (JoyInfo4.buttons > JOYBUTTONS)
|
|
JoyInfo4.buttons = JOYBUTTONS;
|
|
|
|
JoyInfo4.hats = SDL_JoystickNumHats(JoyInfo4.dev);
|
|
if (JoyInfo4.hats > JOYHATS)
|
|
JoyInfo4.hats = JOYHATS;
|
|
|
|
JoyInfo4.balls = SDL_JoystickNumBalls(JoyInfo4.dev);
|
|
|
|
//Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo4.dev), "pad");
|
|
|
|
return JoyInfo4.axises;
|
|
}
|
|
}
|
|
|
|
//
|
|
// I_InitJoystick
|
|
//
|
|
void I_InitJoystick(void)
|
|
{
|
|
SDL_Joystick *newjoy = NULL;
|
|
|
|
//I_ShutdownJoystick();
|
|
//SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
|
|
if (M_CheckParm("-nojoy"))
|
|
return;
|
|
|
|
if (M_CheckParm("-noxinput"))
|
|
SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
|
|
|
|
if (M_CheckParm("-nohidapi"))
|
|
SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
|
|
{
|
|
CONS_Printf("I_InitJoystick()...\n");
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
|
|
{
|
|
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cv_usejoystick.value)
|
|
newjoy = SDL_JoystickOpen(cv_usejoystick.value-1);
|
|
|
|
if (newjoy && (JoyInfo2.dev == newjoy || JoyInfo3.dev == newjoy || JoyInfo4.dev == newjoy)) // don't override an active device
|
|
cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
|
|
else if (newjoy && joy_open(cv_usejoystick.value) != -1)
|
|
{
|
|
// SDL's device indexes are unstable, so cv_usejoystick may not match
|
|
// the actual device index. So let's cheat a bit and find the device's current index.
|
|
JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
|
|
joystick_started = 1;
|
|
}
|
|
else
|
|
{
|
|
if (JoyInfo.oldjoy)
|
|
I_ShutdownJoystick();
|
|
cv_usejoystick.value = 0;
|
|
joystick_started = 0;
|
|
}
|
|
|
|
if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy && JoyInfo3.dev != newjoy && JoyInfo4.dev != newjoy)
|
|
SDL_JoystickClose(newjoy);
|
|
}
|
|
|
|
void I_InitJoystick2(void)
|
|
{
|
|
SDL_Joystick *newjoy = NULL;
|
|
|
|
//I_ShutdownJoystick2();
|
|
//SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
|
|
if (M_CheckParm("-nojoy"))
|
|
return;
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
|
|
{
|
|
CONS_Printf("I_InitJoystick2()...\n");
|
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
|
|
{
|
|
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cv_usejoystick2.value)
|
|
newjoy = SDL_JoystickOpen(cv_usejoystick2.value-1);
|
|
|
|
if (newjoy && (JoyInfo.dev == newjoy || JoyInfo3.dev == newjoy || JoyInfo4.dev == newjoy)) // don't override an active device
|
|
cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
|
|
else if (newjoy && joy_open2(cv_usejoystick2.value) != -1)
|
|
{
|
|
// SDL's device indexes are unstable, so cv_usejoystick may not match
|
|
// the actual device index. So let's cheat a bit and find the device's current index.
|
|
JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
|
|
joystick2_started = 1;
|
|
}
|
|
else
|
|
{
|
|
if (JoyInfo2.oldjoy)
|
|
I_ShutdownJoystick2();
|
|
cv_usejoystick2.value = 0;
|
|
joystick2_started = 0;
|
|
}
|
|
|
|
if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy && JoyInfo3.dev != newjoy && JoyInfo4.dev != newjoy)
|
|
SDL_JoystickClose(newjoy);
|
|
}
|
|
|
|
void I_InitJoystick3(void)
|
|
{
|
|
SDL_Joystick *newjoy = NULL;
|
|
|
|
//I_ShutdownJoystick3();
|
|
//SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
|
|
if (M_CheckParm("-nojoy"))
|
|
return;
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
|
|
{
|
|
CONS_Printf("I_InitJoystick3()...\n");
|
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
|
|
{
|
|
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cv_usejoystick3.value)
|
|
newjoy = SDL_JoystickOpen(cv_usejoystick3.value - 1);
|
|
|
|
if (newjoy && (JoyInfo.dev == newjoy || JoyInfo2.dev == newjoy || JoyInfo4.dev == newjoy)) // don't override an active device
|
|
cv_usejoystick3.value = I_GetJoystickDeviceIndex(JoyInfo3.dev) + 1;
|
|
else if (newjoy && joy_open3(cv_usejoystick3.value) != -1)
|
|
{
|
|
// SDL's device indexes are unstable, so cv_usejoystick may not match
|
|
// the actual device index. So let's cheat a bit and find the device's current index.
|
|
JoyInfo3.oldjoy = I_GetJoystickDeviceIndex(JoyInfo3.dev) + 1;
|
|
joystick3_started = 1;
|
|
}
|
|
else
|
|
{
|
|
if (JoyInfo3.oldjoy)
|
|
I_ShutdownJoystick3();
|
|
cv_usejoystick3.value = 0;
|
|
joystick3_started = 0;
|
|
}
|
|
|
|
if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy && JoyInfo3.dev != newjoy && JoyInfo4.dev != newjoy)
|
|
SDL_JoystickClose(newjoy);
|
|
}
|
|
|
|
void I_InitJoystick4(void)
|
|
{
|
|
SDL_Joystick *newjoy = NULL;
|
|
|
|
//I_ShutdownJoystick4();
|
|
//SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
|
|
if (M_CheckParm("-nojoy"))
|
|
return;
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
|
|
{
|
|
CONS_Printf("I_InitJoystick4()...\n");
|
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
|
|
{
|
|
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cv_usejoystick4.value)
|
|
newjoy = SDL_JoystickOpen(cv_usejoystick4.value - 1);
|
|
|
|
if (newjoy && (JoyInfo.dev == newjoy || JoyInfo2.dev == newjoy || JoyInfo4.dev == newjoy)) // don't override an active device
|
|
cv_usejoystick4.value = I_GetJoystickDeviceIndex(JoyInfo4.dev) + 1;
|
|
else if (newjoy && joy_open4(cv_usejoystick4.value) != -1)
|
|
{
|
|
// SDL's device indexes are unstable, so cv_usejoystick may not match
|
|
// the actual device index. So let's cheat a bit and find the device's current index.
|
|
JoyInfo4.oldjoy = I_GetJoystickDeviceIndex(JoyInfo4.dev) + 1;
|
|
joystick4_started = 1;
|
|
}
|
|
else
|
|
{
|
|
if (JoyInfo4.oldjoy)
|
|
I_ShutdownJoystick4();
|
|
cv_usejoystick4.value = 0;
|
|
joystick4_started = 0;
|
|
}
|
|
|
|
if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy && JoyInfo3.dev != newjoy && JoyInfo4.dev != newjoy)
|
|
SDL_JoystickClose(newjoy);
|
|
}
|
|
|
|
static void I_ShutdownInput(void)
|
|
{
|
|
// Yes, the name is misleading: these send neutral events to
|
|
// clean up the unplugged joystick's input
|
|
// Note these methods are internal to this file, not called elsewhere.
|
|
I_ShutdownJoystick();
|
|
I_ShutdownJoystick2();
|
|
I_ShutdownJoystick3();
|
|
I_ShutdownJoystick4();
|
|
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
|
|
{
|
|
CONS_Printf("Shutting down joy system\n");
|
|
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
|
I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n");
|
|
}
|
|
}
|
|
|
|
INT32 I_NumJoys(void)
|
|
{
|
|
INT32 numjoy = 0;
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
|
|
numjoy = SDL_NumJoysticks();
|
|
return numjoy;
|
|
}
|
|
|
|
static char joyname[255]; // MAX_PATH; joystick name is straight from the driver
|
|
|
|
const char *I_GetJoyName(INT32 joyindex)
|
|
{
|
|
const char *tempname = NULL;
|
|
joyname[0] = 0;
|
|
joyindex--; //SDL's Joystick System starts at 0, not 1
|
|
if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
|
|
{
|
|
tempname = SDL_JoystickNameForIndex(joyindex);
|
|
if (tempname)
|
|
strncpy(joyname, tempname, 255);
|
|
}
|
|
return joyname;
|
|
}
|
|
|
|
#ifndef NOMUMBLE
|
|
#ifdef HAVE_MUMBLE
|
|
// Best Mumble positional audio settings:
|
|
// Minimum distance 3.0 m
|
|
// Bloom 175%
|
|
// Maximum distance 80.0 m
|
|
// Minimum volume 50%
|
|
#define DEG2RAD (0.017453292519943295769236907684883l) // TAU/360 or PI/180
|
|
#define MUMBLEUNIT (64.0f) // FRACUNITS in a Meter
|
|
|
|
static struct {
|
|
#ifdef WINMUMBLE
|
|
UINT32 uiVersion;
|
|
DWORD uiTick;
|
|
#else
|
|
Uint32 uiVersion;
|
|
Uint32 uiTick;
|
|
#endif
|
|
float fAvatarPosition[3];
|
|
float fAvatarFront[3];
|
|
float fAvatarTop[3]; // defaults to Y-is-up (only used for leaning)
|
|
wchar_t name[256]; // game name
|
|
float fCameraPosition[3];
|
|
float fCameraFront[3];
|
|
float fCameraTop[3]; // defaults to Y-is-up (only used for leaning)
|
|
wchar_t identity[256]; // player id
|
|
#ifdef WINMUMBLE
|
|
UINT32 context_len;
|
|
#else
|
|
Uint32 context_len;
|
|
#endif
|
|
unsigned char context[256]; // server/team
|
|
wchar_t description[2048]; // game description
|
|
} *mumble = NULL;
|
|
#endif // HAVE_MUMBLE
|
|
|
|
static void I_SetupMumble(void)
|
|
{
|
|
#ifdef WINMUMBLE
|
|
HANDLE hMap = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink");
|
|
if (!hMap)
|
|
return;
|
|
|
|
mumble = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(*mumble));
|
|
if (!mumble)
|
|
CloseHandle(hMap);
|
|
#elif defined (HAVE_SHM)
|
|
int shmfd;
|
|
char memname[256];
|
|
|
|
snprintf(memname, 256, "/MumbleLink.%d", getuid());
|
|
shmfd = shm_open(memname, O_RDWR, S_IRUSR | S_IWUSR);
|
|
|
|
if(shmfd < 0)
|
|
return;
|
|
|
|
mumble = mmap(NULL, sizeof(*mumble), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
|
|
if (mumble == MAP_FAILED)
|
|
mumble = NULL;
|
|
#endif
|
|
}
|
|
|
|
void I_UpdateMumble(const mobj_t *mobj, const listener_t listener)
|
|
{
|
|
#ifdef HAVE_MUMBLE
|
|
double angle;
|
|
fixed_t anglef;
|
|
|
|
if (!mumble)
|
|
return;
|
|
|
|
if(mumble->uiVersion != 2) {
|
|
wcsncpy(mumble->name, L"SRB2Kart "VERSIONSTRINGW, 256);
|
|
wcsncpy(mumble->description, L"Sonic Robo Blast 2 Kart with integrated Mumble Link support.", 2048);
|
|
mumble->uiVersion = 2;
|
|
}
|
|
mumble->uiTick++;
|
|
|
|
if (!netgame || gamestate != GS_LEVEL) { // Zero out, but never delink.
|
|
mumble->fAvatarPosition[0] = mumble->fAvatarPosition[1] = mumble->fAvatarPosition[2] = 0.0f;
|
|
mumble->fAvatarFront[0] = 1.0f;
|
|
mumble->fAvatarFront[1] = mumble->fAvatarFront[2] = 0.0f;
|
|
mumble->fCameraPosition[0] = mumble->fCameraPosition[1] = mumble->fCameraPosition[2] = 0.0f;
|
|
mumble->fCameraFront[0] = 1.0f;
|
|
mumble->fCameraFront[1] = mumble->fCameraFront[2] = 0.0f;
|
|
return;
|
|
}
|
|
|
|
{
|
|
UINT8 *p = mumble->context;
|
|
WRITEMEM(p, server_context, 8);
|
|
WRITEINT16(p, gamemap);
|
|
mumble->context_len = (UINT32)(p - mumble->context);
|
|
}
|
|
|
|
if (mobj) {
|
|
mumble->fAvatarPosition[0] = FIXED_TO_FLOAT(mobj->x) / MUMBLEUNIT;
|
|
mumble->fAvatarPosition[1] = FIXED_TO_FLOAT(mobj->z) / MUMBLEUNIT;
|
|
mumble->fAvatarPosition[2] = FIXED_TO_FLOAT(mobj->y) / MUMBLEUNIT;
|
|
|
|
anglef = AngleFixed(mobj->angle);
|
|
angle = FIXED_TO_FLOAT(anglef)*DEG2RAD;
|
|
mumble->fAvatarFront[0] = (float)cos(angle);
|
|
mumble->fAvatarFront[1] = 0.0f;
|
|
mumble->fAvatarFront[2] = (float)sin(angle);
|
|
} else {
|
|
mumble->fAvatarPosition[0] = mumble->fAvatarPosition[1] = mumble->fAvatarPosition[2] = 0.0f;
|
|
mumble->fAvatarFront[0] = 1.0f;
|
|
mumble->fAvatarFront[1] = mumble->fAvatarFront[2] = 0.0f;
|
|
}
|
|
|
|
mumble->fCameraPosition[0] = FIXED_TO_FLOAT(listener.x) / MUMBLEUNIT;
|
|
mumble->fCameraPosition[1] = FIXED_TO_FLOAT(listener.z) / MUMBLEUNIT;
|
|
mumble->fCameraPosition[2] = FIXED_TO_FLOAT(listener.y) / MUMBLEUNIT;
|
|
|
|
anglef = AngleFixed(listener.angle);
|
|
angle = FIXED_TO_FLOAT(anglef)*DEG2RAD;
|
|
mumble->fCameraFront[0] = (float)cos(angle);
|
|
mumble->fCameraFront[1] = 0.0f;
|
|
mumble->fCameraFront[2] = (float)sin(angle);
|
|
#else
|
|
(void)mobj;
|
|
(void)listener;
|
|
#endif // HAVE_MUMBLE
|
|
}
|
|
#undef WINMUMBLE
|
|
#endif // NOMUMBLE
|
|
|
|
#ifdef HAVE_TERMIOS
|
|
|
|
void I_GetMouseEvents(void)
|
|
{
|
|
static UINT8 mdata[5];
|
|
static INT32 i = 0,om2b = 0;
|
|
INT32 di, j, mlp, button;
|
|
event_t event;
|
|
const INT32 mswap[8] = {0, 4, 1, 5, 2, 6, 3, 7};
|
|
|
|
if (!mouse2_started) return;
|
|
for (mlp = 0; mlp < 20; mlp++)
|
|
{
|
|
for (; i < 5; i++)
|
|
{
|
|
di = read(fdmouse2, mdata+i, 1);
|
|
if (di == -1) return;
|
|
}
|
|
if ((mdata[0] & 0xf8) != 0x80)
|
|
{
|
|
for (j = 1; j < 5; j++)
|
|
if ((mdata[j] & 0xf8) == 0x80)
|
|
for (i = 0; i < 5-j; i++) // shift
|
|
mdata[i] = mdata[i+j];
|
|
if (i < 5) continue;
|
|
}
|
|
else
|
|
{
|
|
button = mswap[~mdata[0] & 0x07];
|
|
for (j = 0; j < MOUSEBUTTONS; j++)
|
|
{
|
|
if (om2b & (1<<j))
|
|
{
|
|
if (!(button & (1<<j))) //keyup
|
|
{
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_2MOUSE1+j;
|
|
D_PostEvent(&event);
|
|
om2b ^= 1 << j;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (button & (1<<j))
|
|
{
|
|
event.type = ev_keydown;
|
|
event.data1 = KEY_2MOUSE1+j;
|
|
D_PostEvent(&event);
|
|
om2b ^= 1 << j;
|
|
}
|
|
}
|
|
}
|
|
event.data2 = ((SINT8)mdata[1])+((SINT8)mdata[3]);
|
|
event.data3 = ((SINT8)mdata[2])+((SINT8)mdata[4]);
|
|
if (event.data2 && event.data3)
|
|
{
|
|
event.type = ev_mouse2;
|
|
event.data1 = 0;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
i = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// I_ShutdownMouse2
|
|
//
|
|
static void I_ShutdownMouse2(void)
|
|
{
|
|
if (fdmouse2 != -1) close(fdmouse2);
|
|
mouse2_started = 0;
|
|
}
|
|
#elif defined (_WIN32)
|
|
|
|
static HANDLE mouse2filehandle = INVALID_HANDLE_VALUE;
|
|
|
|
static void I_ShutdownMouse2(void)
|
|
{
|
|
event_t event;
|
|
INT32 i;
|
|
|
|
if (mouse2filehandle == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
SetCommMask(mouse2filehandle, 0);
|
|
|
|
EscapeCommFunction(mouse2filehandle, CLRDTR);
|
|
EscapeCommFunction(mouse2filehandle, CLRRTS);
|
|
|
|
PurgeComm(mouse2filehandle, PURGE_TXABORT | PURGE_RXABORT |
|
|
PURGE_TXCLEAR | PURGE_RXCLEAR);
|
|
|
|
CloseHandle(mouse2filehandle);
|
|
|
|
// emulate the up of all mouse buttons
|
|
for (i = 0; i < MOUSEBUTTONS; i++)
|
|
{
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_2MOUSE1+i;
|
|
D_PostEvent(&event);
|
|
}
|
|
|
|
mouse2filehandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
#define MOUSECOMBUFFERSIZE 256
|
|
static INT32 handlermouse2x,handlermouse2y,handlermouse2buttons;
|
|
|
|
static void I_PoolMouse2(void)
|
|
{
|
|
UINT8 buffer[MOUSECOMBUFFERSIZE];
|
|
COMSTAT ComStat;
|
|
DWORD dwErrorFlags;
|
|
DWORD dwLength;
|
|
char dx,dy;
|
|
|
|
static INT32 bytenum;
|
|
static UINT8 combytes[4];
|
|
DWORD i;
|
|
|
|
ClearCommError(mouse2filehandle, &dwErrorFlags, &ComStat);
|
|
dwLength = min(MOUSECOMBUFFERSIZE, ComStat.cbInQue);
|
|
|
|
if (dwLength <= 0)
|
|
return;
|
|
|
|
if (!ReadFile(mouse2filehandle, buffer, dwLength, &dwLength, NULL))
|
|
{
|
|
CONS_Alert(CONS_WARNING, "%s", M_GetText("Read Error on secondary mouse port\n"));
|
|
return;
|
|
}
|
|
|
|
// parse the mouse packets
|
|
for (i = 0; i < dwLength; i++)
|
|
{
|
|
if ((buffer[i] & 64)== 64)
|
|
bytenum = 0;
|
|
|
|
if (bytenum < 4)
|
|
combytes[bytenum] = buffer[i];
|
|
bytenum++;
|
|
|
|
if (bytenum == 1)
|
|
{
|
|
handlermouse2buttons &= ~3;
|
|
handlermouse2buttons |= ((combytes[0] & (32+16)) >> 4);
|
|
}
|
|
else if (bytenum == 3)
|
|
{
|
|
dx = (char)((combytes[0] & 3) << 6);
|
|
dy = (char)((combytes[0] & 12) << 4);
|
|
dx = (char)(dx + combytes[1]);
|
|
dy = (char)(dy + combytes[2]);
|
|
handlermouse2x+= dx;
|
|
handlermouse2y+= dy;
|
|
}
|
|
else if (bytenum == 4) // fourth UINT8 (logitech mouses)
|
|
{
|
|
if (buffer[i] & 32)
|
|
handlermouse2buttons |= 4;
|
|
else
|
|
handlermouse2buttons &= ~4;
|
|
}
|
|
}
|
|
}
|
|
|
|
void I_GetMouseEvents(void)
|
|
{
|
|
static UINT8 lastbuttons2 = 0; //mouse movement
|
|
event_t event;
|
|
|
|
if (mouse2filehandle == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
I_PoolMouse2();
|
|
// post key event for buttons
|
|
if (handlermouse2buttons != lastbuttons2)
|
|
{
|
|
INT32 i, j = 1, k;
|
|
k = (handlermouse2buttons ^ lastbuttons2); // only changed bit to 1
|
|
lastbuttons2 = (UINT8)handlermouse2buttons;
|
|
|
|
for (i = 0; i < MOUSEBUTTONS; i++, j <<= 1)
|
|
if (k & j)
|
|
{
|
|
if (handlermouse2buttons & j)
|
|
event.type = ev_keydown;
|
|
else
|
|
event.type = ev_keyup;
|
|
event.data1 = KEY_2MOUSE1+i;
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
|
|
if (handlermouse2x != 0 || handlermouse2y != 0)
|
|
{
|
|
event.type = ev_mouse2;
|
|
event.data1 = 0;
|
|
// event.data1 = buttons; // not needed
|
|
event.data2 = handlermouse2x << 1;
|
|
event.data3 = -handlermouse2y << 1;
|
|
handlermouse2x = 0;
|
|
handlermouse2y = 0;
|
|
|
|
D_PostEvent(&event);
|
|
}
|
|
}
|
|
#else
|
|
void I_GetMouseEvents(void){};
|
|
#endif
|
|
|
|
//
|
|
// I_StartupMouse2
|
|
//
|
|
void I_StartupMouse2(void)
|
|
{
|
|
#ifdef HAVE_TERMIOS
|
|
struct termios m2tio;
|
|
size_t i;
|
|
INT32 dtr = -1, rts = -1;;
|
|
I_ShutdownMouse2();
|
|
if (cv_usemouse2.value == 0) return;
|
|
if ((fdmouse2 = open(cv_mouse2port.string, O_RDONLY|O_NONBLOCK|O_NOCTTY)) == -1)
|
|
{
|
|
CONS_Printf(M_GetText("Error opening %s!\n"), cv_mouse2port.string);
|
|
return;
|
|
}
|
|
tcflush(fdmouse2, TCIOFLUSH);
|
|
m2tio.c_iflag = IGNBRK;
|
|
m2tio.c_oflag = 0;
|
|
m2tio.c_cflag = CREAD|CLOCAL|HUPCL|CS8|CSTOPB|B1200;
|
|
m2tio.c_lflag = 0;
|
|
m2tio.c_cc[VTIME] = 0;
|
|
m2tio.c_cc[VMIN] = 1;
|
|
tcsetattr(fdmouse2, TCSANOW, &m2tio);
|
|
for (i = 0; i < strlen(cv_mouse2opt.string); i++)
|
|
{
|
|
if (toupper(cv_mouse2opt.string[i]) == 'D')
|
|
{
|
|
if (cv_mouse2opt.string[i+1] == '-')
|
|
dtr = 0;
|
|
else
|
|
dtr = 1;
|
|
}
|
|
if (toupper(cv_mouse2opt.string[i]) == 'R')
|
|
{
|
|
if (cv_mouse2opt.string[i+1] == '-')
|
|
rts = 0;
|
|
else
|
|
rts = 1;
|
|
}
|
|
if (dtr != -1 || rts != -1)
|
|
{
|
|
INT32 c;
|
|
if (!ioctl(fdmouse2, TIOCMGET, &c))
|
|
{
|
|
if (!dtr)
|
|
c &= ~TIOCM_DTR;
|
|
else if (dtr > 0)
|
|
c |= TIOCM_DTR;
|
|
}
|
|
if (!rts)
|
|
c &= ~TIOCM_RTS;
|
|
else if (rts > 0)
|
|
c |= TIOCM_RTS;
|
|
ioctl(fdmouse2, TIOCMSET, &c);
|
|
}
|
|
}
|
|
mouse2_started = 1;
|
|
I_AddExitFunc(I_ShutdownMouse2);
|
|
#elif defined (_WIN32)
|
|
DCB dcb;
|
|
|
|
if (mouse2filehandle != INVALID_HANDLE_VALUE)
|
|
I_ShutdownMouse2();
|
|
|
|
if (cv_usemouse2.value == 0)
|
|
return;
|
|
|
|
if (mouse2filehandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
// COM file handle
|
|
mouse2filehandle = CreateFileA(cv_mouse2port.string, GENERIC_READ | GENERIC_WRITE,
|
|
0, // exclusive access
|
|
NULL, // no security attrs
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (mouse2filehandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
INT32 e = GetLastError();
|
|
if (e == 5)
|
|
CONS_Alert(CONS_ERROR, M_GetText("Can't open %s: Access denied\n"), cv_mouse2port.string);
|
|
else
|
|
CONS_Alert(CONS_ERROR, M_GetText("Can't open %s: error %d\n"), cv_mouse2port.string, e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// getevent when somthing happens
|
|
//SetCommMask(mouse2filehandle, EV_RXCHAR);
|
|
|
|
// buffers
|
|
SetupComm(mouse2filehandle, MOUSECOMBUFFERSIZE, MOUSECOMBUFFERSIZE);
|
|
|
|
// purge buffers
|
|
PurgeComm(mouse2filehandle, PURGE_TXABORT | PURGE_RXABORT
|
|
| PURGE_TXCLEAR | PURGE_RXCLEAR);
|
|
|
|
// setup port to 1200 7N1
|
|
dcb.DCBlength = sizeof (DCB);
|
|
|
|
GetCommState(mouse2filehandle, &dcb);
|
|
|
|
dcb.BaudRate = CBR_1200;
|
|
dcb.ByteSize = 7;
|
|
dcb.Parity = NOPARITY;
|
|
dcb.StopBits = ONESTOPBIT;
|
|
|
|
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
|
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
|
|
|
dcb.fBinary = TRUE;
|
|
dcb.fParity = TRUE;
|
|
|
|
SetCommState(mouse2filehandle, &dcb);
|
|
I_AddExitFunc(I_ShutdownMouse2);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// I_Tactile
|
|
//
|
|
void I_Tactile(FFType pFFType, const JoyFF_t *FFEffect)
|
|
{
|
|
// UNUSED.
|
|
(void)pFFType;
|
|
(void)FFEffect;
|
|
}
|
|
|
|
void I_Tactile2(FFType pFFType, const JoyFF_t *FFEffect)
|
|
{
|
|
// UNUSED.
|
|
(void)pFFType;
|
|
(void)FFEffect;
|
|
}
|
|
|
|
void I_Tactile3(FFType pFFType, const JoyFF_t *FFEffect)
|
|
{
|
|
// UNUSED.
|
|
(void)pFFType;
|
|
(void)FFEffect;
|
|
}
|
|
|
|
void I_Tactile4(FFType pFFType, const JoyFF_t *FFEffect)
|
|
{
|
|
// UNUSED.
|
|
(void)pFFType;
|
|
(void)FFEffect;
|
|
}
|
|
|
|
/** \brief empty ticcmd for player 1
|
|
*/
|
|
static ticcmd_t emptycmd;
|
|
|
|
ticcmd_t *I_BaseTiccmd(void)
|
|
{
|
|
return &emptycmd;
|
|
}
|
|
|
|
/** \brief empty ticcmd for player 2
|
|
*/
|
|
static ticcmd_t emptycmd2;
|
|
|
|
ticcmd_t *I_BaseTiccmd2(void)
|
|
{
|
|
return &emptycmd2;
|
|
}
|
|
|
|
/** \brief empty ticcmd for player 3
|
|
*/
|
|
static ticcmd_t emptycmd3;
|
|
|
|
ticcmd_t *I_BaseTiccmd3(void)
|
|
{
|
|
return &emptycmd3;
|
|
}
|
|
|
|
/** \brief empty ticcmd for player 4
|
|
*/
|
|
static ticcmd_t emptycmd4;
|
|
|
|
ticcmd_t *I_BaseTiccmd4(void)
|
|
{
|
|
return &emptycmd4;
|
|
}
|
|
|
|
#if defined (_WIN32)
|
|
static HMODULE winmm = NULL;
|
|
static DWORD starttickcount = 0; // hack for win2k time bug
|
|
static p_timeGetTime pfntimeGetTime = NULL;
|
|
|
|
// ---------
|
|
// I_GetTime
|
|
// Use the High Resolution Timer if available,
|
|
// else use the multimedia timer which has 1 millisecond precision on Windowz 95,
|
|
// but lower precision on Windows NT
|
|
// ---------
|
|
|
|
tic_t I_GetTime(void)
|
|
{
|
|
tic_t newtics = 0;
|
|
|
|
if (!starttickcount) // high precision timer
|
|
{
|
|
LARGE_INTEGER currtime; // use only LowPart if high resolution counter is not available
|
|
static LARGE_INTEGER basetime = {{0, 0}};
|
|
|
|
// use this if High Resolution timer is found
|
|
static LARGE_INTEGER frequency;
|
|
|
|
if (!basetime.LowPart)
|
|
{
|
|
if (!QueryPerformanceFrequency(&frequency))
|
|
frequency.QuadPart = 0;
|
|
else
|
|
QueryPerformanceCounter(&basetime);
|
|
}
|
|
|
|
if (frequency.LowPart && QueryPerformanceCounter(&currtime))
|
|
{
|
|
newtics = (INT32)((currtime.QuadPart - basetime.QuadPart) * NEWTICRATE
|
|
/ frequency.QuadPart);
|
|
}
|
|
else if (pfntimeGetTime)
|
|
{
|
|
currtime.LowPart = pfntimeGetTime();
|
|
if (!basetime.LowPart)
|
|
basetime.LowPart = currtime.LowPart;
|
|
newtics = ((currtime.LowPart - basetime.LowPart)/(1000/NEWTICRATE));
|
|
}
|
|
}
|
|
else
|
|
newtics = (GetTickCount() - starttickcount)/(1000/NEWTICRATE);
|
|
|
|
return newtics;
|
|
}
|
|
|
|
static void I_ShutdownTimer(void)
|
|
{
|
|
pfntimeGetTime = NULL;
|
|
if (winmm)
|
|
{
|
|
p_timeEndPeriod pfntimeEndPeriod = (p_timeEndPeriod)GetProcAddress(winmm, "timeEndPeriod");
|
|
if (pfntimeEndPeriod)
|
|
pfntimeEndPeriod(1);
|
|
FreeLibrary(winmm);
|
|
winmm = NULL;
|
|
}
|
|
}
|
|
#else
|
|
//
|
|
// I_GetTime
|
|
// returns time in 1/TICRATE second tics
|
|
//
|
|
tic_t I_GetTime (void)
|
|
{
|
|
static Uint32 basetime = 0;
|
|
Uint32 ticks = SDL_GetTicks();
|
|
|
|
if (!basetime)
|
|
basetime = ticks;
|
|
|
|
ticks -= basetime;
|
|
|
|
ticks = (ticks*TICRATE);
|
|
|
|
ticks = (ticks/1000);
|
|
|
|
return (tic_t)ticks;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//I_StartupTimer
|
|
//
|
|
void I_StartupTimer(void)
|
|
{
|
|
#ifdef _WIN32
|
|
// for win2k time bug
|
|
if (M_CheckParm("-gettickcount"))
|
|
{
|
|
starttickcount = GetTickCount();
|
|
CONS_Printf("%s", M_GetText("Using GetTickCount()\n"));
|
|
}
|
|
winmm = LoadLibraryA("winmm.dll");
|
|
if (winmm)
|
|
{
|
|
p_timeEndPeriod pfntimeBeginPeriod = (p_timeEndPeriod)GetProcAddress(winmm, "timeBeginPeriod");
|
|
if (pfntimeBeginPeriod)
|
|
pfntimeBeginPeriod(1);
|
|
pfntimeGetTime = (p_timeGetTime)GetProcAddress(winmm, "timeGetTime");
|
|
}
|
|
I_AddExitFunc(I_ShutdownTimer);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
void I_Sleep(void)
|
|
{
|
|
if (cv_sleep.value != -1)
|
|
SDL_Delay(cv_sleep.value);
|
|
}
|
|
|
|
INT32 I_StartupSystem(void)
|
|
{
|
|
SDL_version SDLcompiled;
|
|
SDL_version SDLlinked;
|
|
SDL_VERSION(&SDLcompiled)
|
|
SDL_GetVersion(&SDLlinked);
|
|
I_StartupConsole();
|
|
I_OutputMsg("Compiled for SDL version: %d.%d.%d\n",
|
|
SDLcompiled.major, SDLcompiled.minor, SDLcompiled.patch);
|
|
I_OutputMsg("Linked with SDL version: %d.%d.%d\n",
|
|
SDLlinked.major, SDLlinked.minor, SDLlinked.patch);
|
|
if (SDL_Init(0) < 0)
|
|
I_Error("SRB2: SDL System Error: %s", SDL_GetError()); //Alam: Oh no....
|
|
#ifndef NOMUMBLE
|
|
I_SetupMumble();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// I_Quit
|
|
//
|
|
void I_Quit(void)
|
|
{
|
|
static SDL_bool quiting = SDL_FALSE;
|
|
|
|
/* prevent recursive I_Quit() */
|
|
if (quiting) goto death;
|
|
SDLforceUngrabMouse();
|
|
quiting = SDL_FALSE;
|
|
I_ShutdownConsole();
|
|
M_SaveConfig(NULL); //save game config, cvars..
|
|
#ifndef NONET
|
|
D_SaveBan(); // save the ban list
|
|
#endif
|
|
G_SaveGameData(false); // Tails 12-08-2002
|
|
//added:16-02-98: when recording a demo, should exit using 'q' key,
|
|
// but sometimes we forget and use 'F10'.. so save here too.
|
|
|
|
if (demorecording)
|
|
G_CheckDemoStatus();
|
|
if (metalrecording)
|
|
G_StopMetalRecording();
|
|
|
|
D_QuitNetGame();
|
|
I_ShutdownMusic();
|
|
I_ShutdownSound();
|
|
I_ShutdownCD();
|
|
// use this for 1.28 19990220 by Kin
|
|
I_ShutdownGraphics();
|
|
I_ShutdownInput();
|
|
I_ShutdownSystem();
|
|
SDL_Quit();
|
|
/* if option -noendtxt is set, don't print the text */
|
|
if (!M_CheckParm("-noendtxt") && W_CheckNumForName("ENDOOM") != LUMPERROR)
|
|
{
|
|
printf("\r");
|
|
ShowEndTxt();
|
|
}
|
|
if (myargmalloc)
|
|
free(myargv); // Deallocate allocated memory
|
|
death:
|
|
W_Shutdown();
|
|
exit(0);
|
|
}
|
|
|
|
void I_WaitVBL(INT32 count)
|
|
{
|
|
count = 1;
|
|
SDL_Delay(count);
|
|
}
|
|
|
|
void I_BeginRead(void)
|
|
{
|
|
}
|
|
|
|
void I_EndRead(void)
|
|
{
|
|
}
|
|
|
|
//
|
|
// I_Error
|
|
//
|
|
/** \brief phuck recursive errors
|
|
*/
|
|
static INT32 errorcount = 0;
|
|
|
|
/** \brief recursive error detecting
|
|
*/
|
|
static boolean shutdowning = false;
|
|
|
|
void I_Error(const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char buffer[8192];
|
|
|
|
// recursive error detecting
|
|
if (shutdowning)
|
|
{
|
|
errorcount++;
|
|
if (errorcount == 1)
|
|
SDLforceUngrabMouse();
|
|
// try to shutdown each subsystem separately
|
|
if (errorcount == 2)
|
|
I_ShutdownMusic();
|
|
if (errorcount == 3)
|
|
I_ShutdownSound();
|
|
if (errorcount == 4)
|
|
I_ShutdownCD();
|
|
if (errorcount == 5)
|
|
I_ShutdownGraphics();
|
|
if (errorcount == 6)
|
|
I_ShutdownInput();
|
|
if (errorcount == 7)
|
|
I_ShutdownSystem();
|
|
if (errorcount == 8)
|
|
SDL_Quit();
|
|
if (errorcount == 9)
|
|
{
|
|
M_SaveConfig(NULL);
|
|
G_SaveGameData(false);
|
|
}
|
|
if (errorcount > 20)
|
|
{
|
|
va_start(argptr, error);
|
|
vsprintf(buffer, error, argptr);
|
|
va_end(argptr);
|
|
// Implement message box with SDL_ShowSimpleMessageBox,
|
|
// which should fail gracefully if it can't put a message box up
|
|
// on the target system
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
|
|
"SRB2Kart "VERSIONSTRING" Recursive Error",
|
|
buffer, NULL);
|
|
|
|
W_Shutdown();
|
|
exit(-1); // recursive errors detected
|
|
}
|
|
}
|
|
|
|
shutdowning = true;
|
|
|
|
// Display error message in the console before we start shutting it down
|
|
va_start(argptr, error);
|
|
vsprintf(buffer, error, argptr);
|
|
va_end(argptr);
|
|
I_OutputMsg("\nI_Error(): %s\n", buffer);
|
|
// ---
|
|
|
|
I_ShutdownConsole();
|
|
|
|
M_SaveConfig(NULL); // save game config, cvars..
|
|
#ifndef NONET
|
|
D_SaveBan(); // save the ban list
|
|
#endif
|
|
G_SaveGameData(false); // Tails 12-08-2002
|
|
|
|
// Shutdown. Here might be other errors.
|
|
if (demorecording)
|
|
G_CheckDemoStatus();
|
|
if (metalrecording)
|
|
G_StopMetalRecording();
|
|
|
|
D_QuitNetGame();
|
|
I_ShutdownMusic();
|
|
I_ShutdownSound();
|
|
I_ShutdownCD();
|
|
// use this for 1.28 19990220 by Kin
|
|
I_ShutdownGraphics();
|
|
I_ShutdownInput();
|
|
I_ShutdownSystem();
|
|
SDL_Quit();
|
|
|
|
// Implement message box with SDL_ShowSimpleMessageBox,
|
|
// which should fail gracefully if it can't put a message box up
|
|
// on the target system
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
|
|
"SRB2Kart "VERSIONSTRING" Error",
|
|
buffer, NULL);
|
|
// Note that SDL_ShowSimpleMessageBox does *not* require SDL to be
|
|
// initialized at the time, so calling it after SDL_Quit() is
|
|
// perfectly okay! In addition, we do this on purpose so the
|
|
// fullscreen window is closed before displaying the error message
|
|
// in case the fullscreen window blocks it for some absurd reason.
|
|
|
|
W_Shutdown();
|
|
|
|
#if defined (PARANOIA) && defined (__CYGWIN__)
|
|
*(INT32 *)2 = 4; //Alam: Debug!
|
|
#endif
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
/** \brief quit function table
|
|
*/
|
|
static quitfuncptr quit_funcs[MAX_QUIT_FUNCS]; /* initialized to all bits 0 */
|
|
|
|
//
|
|
// Adds a function to the list that need to be called by I_SystemShutdown().
|
|
//
|
|
void I_AddExitFunc(void (*func)())
|
|
{
|
|
INT32 c;
|
|
|
|
for (c = 0; c < MAX_QUIT_FUNCS; c++)
|
|
{
|
|
if (!quit_funcs[c])
|
|
{
|
|
quit_funcs[c] = func;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Removes a function from the list that need to be called by
|
|
// I_SystemShutdown().
|
|
//
|
|
void I_RemoveExitFunc(void (*func)())
|
|
{
|
|
INT32 c;
|
|
|
|
for (c = 0; c < MAX_QUIT_FUNCS; c++)
|
|
{
|
|
if (quit_funcs[c] == func)
|
|
{
|
|
while (c < MAX_QUIT_FUNCS-1)
|
|
{
|
|
quit_funcs[c] = quit_funcs[c+1];
|
|
c++;
|
|
}
|
|
quit_funcs[MAX_QUIT_FUNCS-1] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Closes down everything. This includes restoring the initial
|
|
// palette and video mode, and removing whatever mouse, keyboard, and
|
|
// timer routines have been installed.
|
|
//
|
|
// NOTE: Shutdown user funcs are effectively called in reverse order.
|
|
//
|
|
void I_ShutdownSystem(void)
|
|
{
|
|
INT32 c;
|
|
|
|
for (c = MAX_QUIT_FUNCS-1; c >= 0; c--)
|
|
if (quit_funcs[c])
|
|
(*quit_funcs[c])();
|
|
#ifdef LOGMESSAGES
|
|
if (logstream)
|
|
{
|
|
I_OutputMsg("I_ShutdownSystem(): end of logstream.\n");
|
|
fclose(logstream);
|
|
logstream = NULL;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void I_GetDiskFreeSpace(INT64 *freespace)
|
|
{
|
|
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
|
|
#if defined (SOLARIS) || defined (__HAIKU__)
|
|
*freespace = INT32_MAX;
|
|
return;
|
|
#else // Both Linux and BSD have this, apparently.
|
|
struct statfs stfs;
|
|
if (statfs(".", &stfs) == -1)
|
|
{
|
|
*freespace = INT32_MAX;
|
|
return;
|
|
}
|
|
*freespace = stfs.f_bavail * stfs.f_bsize;
|
|
#endif
|
|
#elif defined (_WIN32)
|
|
static p_GetDiskFreeSpaceExA pfnGetDiskFreeSpaceEx = NULL;
|
|
static boolean testwin95 = false;
|
|
ULARGE_INTEGER usedbytes, lfreespace;
|
|
|
|
if (!testwin95)
|
|
{
|
|
pfnGetDiskFreeSpaceEx = (p_GetDiskFreeSpaceExA)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA");
|
|
testwin95 = true;
|
|
}
|
|
if (pfnGetDiskFreeSpaceEx)
|
|
{
|
|
if (pfnGetDiskFreeSpaceEx(NULL, &lfreespace, &usedbytes, NULL))
|
|
*freespace = lfreespace.QuadPart;
|
|
else
|
|
*freespace = INT32_MAX;
|
|
}
|
|
else
|
|
{
|
|
DWORD SectorsPerCluster, BytesPerSector, NumberOfFreeClusters, TotalNumberOfClusters;
|
|
GetDiskFreeSpace(NULL, &SectorsPerCluster, &BytesPerSector,
|
|
&NumberOfFreeClusters, &TotalNumberOfClusters);
|
|
*freespace = BytesPerSector*SectorsPerCluster*NumberOfFreeClusters;
|
|
}
|
|
#else // Dummy for platform independent; 1GB should be enough
|
|
*freespace = 1024*1024*1024;
|
|
#endif
|
|
}
|
|
|
|
char *I_GetUserName(void)
|
|
{
|
|
static char username[MAXPLAYERNAME];
|
|
char *p;
|
|
#ifdef _WIN32
|
|
DWORD i = MAXPLAYERNAME;
|
|
|
|
if (!GetUserNameA(username, &i))
|
|
#endif
|
|
{
|
|
p = I_GetEnv("USER");
|
|
if (!p)
|
|
{
|
|
p = I_GetEnv("user");
|
|
if (!p)
|
|
{
|
|
p = I_GetEnv("USERNAME");
|
|
if (!p)
|
|
{
|
|
p = I_GetEnv("username");
|
|
if (!p)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
strncpy(username, p, MAXPLAYERNAME);
|
|
}
|
|
|
|
|
|
if (strcmp(username, "") != 0)
|
|
return username;
|
|
return NULL; // dummy for platform independent version
|
|
}
|
|
|
|
INT32 I_mkdir(const char *dirname, INT32 unixright)
|
|
{
|
|
//[segabor]
|
|
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) || defined (__CYGWIN__) || defined (__OS2__)
|
|
return mkdir(dirname, unixright);
|
|
#elif defined (_WIN32)
|
|
UNREFERENCED_PARAMETER(unixright); /// \todo should implement ntright under nt...
|
|
return CreateDirectoryA(dirname, NULL);
|
|
#else
|
|
(void)dirname;
|
|
(void)unixright;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
char *I_GetEnv(const char *name)
|
|
{
|
|
#ifdef NEED_SDL_GETENV
|
|
return SDL_getenv(name);
|
|
#else
|
|
return getenv(name);
|
|
#endif
|
|
}
|
|
|
|
INT32 I_PutEnv(char *variable)
|
|
{
|
|
#ifdef NEED_SDL_GETENV
|
|
return SDL_putenv(variable);
|
|
#else
|
|
return putenv(variable);
|
|
#endif
|
|
}
|
|
|
|
INT32 I_ClipboardCopy(const char *data, size_t size)
|
|
{
|
|
char storage[256];
|
|
if (size > 255)
|
|
size = 255;
|
|
memcpy(storage, data, size);
|
|
storage[size] = 0;
|
|
|
|
if (SDL_SetClipboardText(storage))
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
const char *I_ClipboardPaste(void)
|
|
{
|
|
static char clipboard_modified[256];
|
|
char *clipboard_contents, *i = clipboard_modified;
|
|
|
|
if (!SDL_HasClipboardText())
|
|
return NULL;
|
|
clipboard_contents = SDL_GetClipboardText();
|
|
memcpy(clipboard_modified, clipboard_contents, 255);
|
|
SDL_free(clipboard_contents);
|
|
clipboard_modified[255] = 0;
|
|
|
|
while (*i)
|
|
{
|
|
if (*i == '\n' || *i == '\r')
|
|
{ // End on newline
|
|
*i = 0;
|
|
break;
|
|
}
|
|
else if (*i == '\t')
|
|
*i = ' '; // Tabs become spaces
|
|
else if (*i < 32 || (unsigned)*i > 127)
|
|
*i = '?'; // Nonprintable chars become question marks
|
|
++i;
|
|
}
|
|
return (const char *)&clipboard_modified;
|
|
}
|
|
|
|
/** \brief The isWadPathOk function
|
|
|
|
\param path string path to check
|
|
|
|
\return if true, wad file found
|
|
|
|
|
|
*/
|
|
static boolean isWadPathOk(const char *path)
|
|
{
|
|
char *wad3path = malloc(256);
|
|
|
|
if (!wad3path)
|
|
return false;
|
|
|
|
sprintf(wad3path, pandf, path, WADKEYWORD1);
|
|
|
|
if (FIL_ReadFileOK(wad3path))
|
|
{
|
|
free(wad3path);
|
|
return true;
|
|
}
|
|
|
|
sprintf(wad3path, pandf, path, WADKEYWORD2);
|
|
|
|
if (FIL_ReadFileOK(wad3path))
|
|
{
|
|
free(wad3path);
|
|
return true;
|
|
}
|
|
|
|
free(wad3path);
|
|
return false;
|
|
}
|
|
|
|
static void pathonly(char *s)
|
|
{
|
|
size_t j;
|
|
|
|
for (j = strlen(s); j != (size_t)-1; j--)
|
|
if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/'))
|
|
{
|
|
if (s[j] == ':') s[j+1] = 0;
|
|
else s[j] = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/** \brief search for srb2.srb in the given path
|
|
|
|
\param searchDir starting path
|
|
|
|
\return WAD path if not NULL
|
|
|
|
|
|
*/
|
|
static const char *searchWad(const char *searchDir)
|
|
{
|
|
static char tempsw[256] = "";
|
|
filestatus_t fstemp;
|
|
|
|
strcpy(tempsw, WADKEYWORD1);
|
|
fstemp = filesearch(tempsw,searchDir,NULL,true,20);
|
|
if (fstemp == FS_FOUND)
|
|
{
|
|
pathonly(tempsw);
|
|
return tempsw;
|
|
}
|
|
|
|
strcpy(tempsw, WADKEYWORD2);
|
|
fstemp = filesearch(tempsw, searchDir, NULL, true, 20);
|
|
if (fstemp == FS_FOUND)
|
|
{
|
|
pathonly(tempsw);
|
|
return tempsw;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/** \brief go through all possible paths and look for srb2.srb
|
|
|
|
\return path to srb2.srb if any
|
|
*/
|
|
static const char *locateWad(void)
|
|
{
|
|
const char *envstr;
|
|
const char *WadPath;
|
|
|
|
I_OutputMsg("SRB2WADDIR");
|
|
// does SRB2WADDIR exist?
|
|
if (((envstr = I_GetEnv("SRB2WADDIR")) != NULL) && isWadPathOk(envstr))
|
|
return envstr;
|
|
|
|
#ifndef NOCWD
|
|
I_OutputMsg(",.");
|
|
// examine current dir
|
|
strcpy(returnWadPath, ".");
|
|
if (isWadPathOk(returnWadPath))
|
|
return NULL;
|
|
#endif
|
|
|
|
|
|
#ifdef CMAKECONFIG
|
|
#ifndef NDEBUG
|
|
I_OutputMsg(","CMAKE_ASSETS_DIR);
|
|
strcpy(returnWadPath, CMAKE_ASSETS_DIR);
|
|
if (isWadPathOk(returnWadPath))
|
|
{
|
|
return returnWadPath;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
OSX_GetResourcesPath(returnWadPath);
|
|
I_OutputMsg(",%s", returnWadPath);
|
|
if (isWadPathOk(returnWadPath))
|
|
{
|
|
return returnWadPath;
|
|
}
|
|
#endif
|
|
|
|
// examine default dirs
|
|
#ifdef DEFAULTWADLOCATION1
|
|
I_OutputMsg(","DEFAULTWADLOCATION1);
|
|
strcpy(returnWadPath, DEFAULTWADLOCATION1);
|
|
if (isWadPathOk(returnWadPath))
|
|
return returnWadPath;
|
|
#endif
|
|
#ifdef DEFAULTWADLOCATION2
|
|
I_OutputMsg(","DEFAULTWADLOCATION2);
|
|
strcpy(returnWadPath, DEFAULTWADLOCATION2);
|
|
if (isWadPathOk(returnWadPath))
|
|
return returnWadPath;
|
|
#endif
|
|
#ifdef DEFAULTWADLOCATION3
|
|
I_OutputMsg(","DEFAULTWADLOCATION3);
|
|
strcpy(returnWadPath, DEFAULTWADLOCATION3);
|
|
if (isWadPathOk(returnWadPath))
|
|
return returnWadPath;
|
|
#endif
|
|
#ifdef DEFAULTWADLOCATION4
|
|
I_OutputMsg(","DEFAULTWADLOCATION4);
|
|
strcpy(returnWadPath, DEFAULTWADLOCATION4);
|
|
if (isWadPathOk(returnWadPath))
|
|
return returnWadPath;
|
|
#endif
|
|
#ifdef DEFAULTWADLOCATION5
|
|
I_OutputMsg(","DEFAULTWADLOCATION5);
|
|
strcpy(returnWadPath, DEFAULTWADLOCATION5);
|
|
if (isWadPathOk(returnWadPath))
|
|
return returnWadPath;
|
|
#endif
|
|
#ifdef DEFAULTWADLOCATION6
|
|
I_OutputMsg(","DEFAULTWADLOCATION6);
|
|
strcpy(returnWadPath, DEFAULTWADLOCATION6);
|
|
if (isWadPathOk(returnWadPath))
|
|
return returnWadPath;
|
|
#endif
|
|
#ifdef DEFAULTWADLOCATION7
|
|
I_OutputMsg(","DEFAULTWADLOCATION7);
|
|
strcpy(returnWadPath, DEFAULTWADLOCATION7);
|
|
if (isWadPathOk(returnWadPath))
|
|
return returnWadPath;
|
|
#endif
|
|
#ifndef NOHOME
|
|
// find in $HOME
|
|
I_OutputMsg(",HOME");
|
|
if ((envstr = I_GetEnv("HOME")) != NULL)
|
|
{
|
|
WadPath = searchWad(envstr);
|
|
if (WadPath)
|
|
return WadPath;
|
|
}
|
|
#endif
|
|
#ifdef DEFAULTSEARCHPATH1
|
|
// find in /usr/local
|
|
I_OutputMsg(", in:"DEFAULTSEARCHPATH1);
|
|
WadPath = searchWad(DEFAULTSEARCHPATH1);
|
|
if (WadPath)
|
|
return WadPath;
|
|
#endif
|
|
#ifdef DEFAULTSEARCHPATH2
|
|
// find in /usr/games
|
|
I_OutputMsg(", in:"DEFAULTSEARCHPATH2);
|
|
WadPath = searchWad(DEFAULTSEARCHPATH2);
|
|
if (WadPath)
|
|
return WadPath;
|
|
#endif
|
|
#ifdef DEFAULTSEARCHPATH3
|
|
// find in ???
|
|
I_OutputMsg(", in:"DEFAULTSEARCHPATH3);
|
|
WadPath = searchWad(DEFAULTSEARCHPATH3);
|
|
if (WadPath)
|
|
return WadPath;
|
|
#endif
|
|
// if nothing was found
|
|
return NULL;
|
|
}
|
|
|
|
const char *I_LocateWad(void)
|
|
{
|
|
const char *waddir;
|
|
|
|
I_OutputMsg("Looking for WADs in: ");
|
|
waddir = locateWad();
|
|
I_OutputMsg("\n");
|
|
|
|
if (waddir)
|
|
{
|
|
// change to the directory where we found srb2.srb
|
|
#if defined (_WIN32)
|
|
SetCurrentDirectoryA(waddir);
|
|
#else
|
|
if (chdir(waddir) == -1)
|
|
I_OutputMsg("Couldn't change working directory\n");
|
|
#endif
|
|
}
|
|
return waddir;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
#define MEMINFO_FILE "/proc/meminfo"
|
|
#define MEMTOTAL "MemTotal:"
|
|
#define MEMAVAILABLE "MemAvailable:"
|
|
#define MEMFREE "MemFree:"
|
|
#define CACHED "Cached:"
|
|
#define BUFFERS "Buffers:"
|
|
#define SHMEM "Shmem:"
|
|
|
|
/* Parse the contents of /proc/meminfo (in buf), return value of "name"
|
|
* (example: MemTotal) */
|
|
static long get_entry(const char* name, const char* buf)
|
|
{
|
|
long val;
|
|
char* hit = strstr(buf, name);
|
|
if (hit == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
errno = 0;
|
|
val = strtol(hit + strlen(name), NULL, 10);
|
|
if (errno != 0) {
|
|
CONS_Alert(CONS_ERROR, M_GetText("get_entry: strtol() failed: %s\n"), strerror(errno));
|
|
return -1;
|
|
}
|
|
return val;
|
|
}
|
|
#endif
|
|
|
|
// quick fix for compil
|
|
UINT32 I_GetFreeMem(UINT32 *total)
|
|
{
|
|
#ifdef FREEBSD
|
|
struct vmmeter sum;
|
|
kvm_t *kd;
|
|
struct nlist namelist[] =
|
|
{
|
|
#define X_SUM 0
|
|
{"_cnt"},
|
|
{NULL}
|
|
};
|
|
if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
|
|
{
|
|
if (total)
|
|
*total = 0L;
|
|
return 0;
|
|
}
|
|
if (kvm_nlist(kd, namelist) != 0)
|
|
{
|
|
kvm_close (kd);
|
|
if (total)
|
|
*total = 0L;
|
|
return 0;
|
|
}
|
|
if (kvm_read(kd, namelist[X_SUM].n_value, &sum,
|
|
sizeof (sum)) != sizeof (sum))
|
|
{
|
|
kvm_close(kd);
|
|
if (total)
|
|
*total = 0L;
|
|
return 0;
|
|
}
|
|
kvm_close(kd);
|
|
|
|
if (total)
|
|
*total = sum.v_page_count * sum.v_page_size;
|
|
return sum.v_free_count * sum.v_page_size;
|
|
#elif defined (SOLARIS)
|
|
/* Just guess */
|
|
if (total)
|
|
*total = 32 << 20;
|
|
return 32 << 20;
|
|
#elif defined (_WIN32)
|
|
MEMORYSTATUS info;
|
|
|
|
info.dwLength = sizeof (MEMORYSTATUS);
|
|
GlobalMemoryStatus( &info );
|
|
if (total)
|
|
*total = (UINT32)info.dwTotalPhys;
|
|
return (UINT32)info.dwAvailPhys;
|
|
#elif defined (__OS2__)
|
|
UINT32 pr_arena;
|
|
|
|
if (total)
|
|
DosQuerySysInfo( QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
|
|
(PVOID) total, sizeof (UINT32));
|
|
DosQuerySysInfo( QSV_MAXPRMEM, QSV_MAXPRMEM,
|
|
(PVOID) &pr_arena, sizeof (UINT32));
|
|
|
|
return pr_arena;
|
|
#elif defined (__linux__)
|
|
/* Linux */
|
|
char buf[1024];
|
|
char *memTag;
|
|
UINT32 freeKBytes;
|
|
UINT32 totalKBytes;
|
|
INT32 n;
|
|
INT32 meminfo_fd = -1;
|
|
long Cached;
|
|
long MemFree;
|
|
long Buffers;
|
|
long Shmem;
|
|
long MemAvailable = -1;
|
|
|
|
meminfo_fd = open(MEMINFO_FILE, O_RDONLY);
|
|
n = read(meminfo_fd, buf, 1023);
|
|
close(meminfo_fd);
|
|
|
|
if (n < 0)
|
|
{
|
|
// Error
|
|
if (total)
|
|
*total = 0L;
|
|
return 0;
|
|
}
|
|
|
|
buf[n] = '\0';
|
|
if ((memTag = strstr(buf, MEMTOTAL)) == NULL)
|
|
{
|
|
// Error
|
|
if (total)
|
|
*total = 0L;
|
|
return 0;
|
|
}
|
|
|
|
memTag += sizeof (MEMTOTAL);
|
|
totalKBytes = atoi(memTag);
|
|
|
|
if ((memTag = strstr(buf, MEMAVAILABLE)) == NULL)
|
|
{
|
|
Cached = get_entry(CACHED, buf);
|
|
MemFree = get_entry(MEMFREE, buf);
|
|
Buffers = get_entry(BUFFERS, buf);
|
|
Shmem = get_entry(SHMEM, buf);
|
|
MemAvailable = Cached + MemFree + Buffers - Shmem;
|
|
|
|
if (MemAvailable == -1)
|
|
{
|
|
// Error
|
|
if (total)
|
|
*total = 0L;
|
|
return 0;
|
|
}
|
|
freeKBytes = MemAvailable;
|
|
}
|
|
else
|
|
{
|
|
memTag += sizeof (MEMAVAILABLE);
|
|
freeKBytes = atoi(memTag);
|
|
}
|
|
|
|
if (total)
|
|
*total = totalKBytes << 10;
|
|
return freeKBytes << 10;
|
|
#else
|
|
// Guess 48 MB.
|
|
if (total)
|
|
*total = 48<<20;
|
|
return 48<<20;
|
|
#endif
|
|
}
|
|
|
|
const CPUInfoFlags *I_CPUInfo(void)
|
|
{
|
|
#if defined (_WIN32)
|
|
static CPUInfoFlags WIN_CPUInfo;
|
|
SYSTEM_INFO SI;
|
|
p_IsProcessorFeaturePresent pfnCPUID = (p_IsProcessorFeaturePresent)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsProcessorFeaturePresent");
|
|
|
|
ZeroMemory(&WIN_CPUInfo,sizeof (WIN_CPUInfo));
|
|
if (pfnCPUID)
|
|
{
|
|
WIN_CPUInfo.FPPE = pfnCPUID( 0); //PF_FLOATING_POINT_PRECISION_ERRATA
|
|
WIN_CPUInfo.FPE = pfnCPUID( 1); //PF_FLOATING_POINT_EMULATED
|
|
WIN_CPUInfo.cmpxchg = pfnCPUID( 2); //PF_COMPARE_EXCHANGE_DOUBLE
|
|
WIN_CPUInfo.MMX = pfnCPUID( 3); //PF_MMX_INSTRUCTIONS_AVAILABLE
|
|
WIN_CPUInfo.PPCMM64 = pfnCPUID( 4); //PF_PPC_MOVEMEM_64BIT_OK
|
|
WIN_CPUInfo.ALPHAbyte = pfnCPUID( 5); //PF_ALPHA_BYTE_INSTRUCTIONS
|
|
WIN_CPUInfo.SSE = pfnCPUID( 6); //PF_XMMI_INSTRUCTIONS_AVAILABLE
|
|
WIN_CPUInfo.AMD3DNow = pfnCPUID( 7); //PF_3DNOW_INSTRUCTIONS_AVAILABLE
|
|
WIN_CPUInfo.RDTSC = pfnCPUID( 8); //PF_RDTSC_INSTRUCTION_AVAILABLE
|
|
WIN_CPUInfo.PAE = pfnCPUID( 9); //PF_PAE_ENABLED
|
|
WIN_CPUInfo.SSE2 = pfnCPUID(10); //PF_XMMI64_INSTRUCTIONS_AVAILABLE
|
|
//WIN_CPUInfo.blank = pfnCPUID(11); //PF_SSE_DAZ_MODE_AVAILABLE
|
|
WIN_CPUInfo.DEP = pfnCPUID(12); //PF_NX_ENABLED
|
|
WIN_CPUInfo.SSE3 = pfnCPUID(13); //PF_SSE3_INSTRUCTIONS_AVAILABLE
|
|
WIN_CPUInfo.cmpxchg16b = pfnCPUID(14); //PF_COMPARE_EXCHANGE128
|
|
WIN_CPUInfo.cmp8xchg16 = pfnCPUID(15); //PF_COMPARE64_EXCHANGE128
|
|
WIN_CPUInfo.PFC = pfnCPUID(16); //PF_CHANNELS_ENABLED
|
|
}
|
|
#ifdef HAVE_SDLCPUINFO
|
|
else
|
|
{
|
|
WIN_CPUInfo.RDTSC = SDL_HasRDTSC();
|
|
WIN_CPUInfo.MMX = SDL_HasMMX();
|
|
WIN_CPUInfo.AMD3DNow = SDL_Has3DNow();
|
|
WIN_CPUInfo.SSE = SDL_HasSSE();
|
|
WIN_CPUInfo.SSE2 = SDL_HasSSE2();
|
|
WIN_CPUInfo.AltiVec = SDL_HasAltiVec();
|
|
}
|
|
WIN_CPUInfo.MMXExt = SDL_FALSE; //SDL_HasMMXExt(); No longer in SDL2
|
|
WIN_CPUInfo.AMD3DNowExt = SDL_FALSE; //SDL_Has3DNowExt(); No longer in SDL2
|
|
#endif
|
|
GetSystemInfo(&SI);
|
|
WIN_CPUInfo.CPUs = SI.dwNumberOfProcessors;
|
|
WIN_CPUInfo.IA64 = (SI.dwProcessorType == 2200); // PROCESSOR_INTEL_IA64
|
|
WIN_CPUInfo.AMD64 = (SI.dwProcessorType == 8664); // PROCESSOR_AMD_X8664
|
|
return &WIN_CPUInfo;
|
|
#elif defined (HAVE_SDLCPUINFO)
|
|
static CPUInfoFlags SDL_CPUInfo;
|
|
memset(&SDL_CPUInfo,0,sizeof (CPUInfoFlags));
|
|
SDL_CPUInfo.RDTSC = SDL_HasRDTSC();
|
|
SDL_CPUInfo.MMX = SDL_HasMMX();
|
|
SDL_CPUInfo.MMXExt = SDL_FALSE; //SDL_HasMMXExt(); No longer in SDL2
|
|
SDL_CPUInfo.AMD3DNow = SDL_Has3DNow();
|
|
SDL_CPUInfo.AMD3DNowExt = SDL_FALSE; //SDL_Has3DNowExt(); No longer in SDL2
|
|
SDL_CPUInfo.SSE = SDL_HasSSE();
|
|
SDL_CPUInfo.SSE2 = SDL_HasSSE2();
|
|
SDL_CPUInfo.AltiVec = SDL_HasAltiVec();
|
|
return &SDL_CPUInfo;
|
|
#else
|
|
return NULL; /// \todo CPUID asm
|
|
#endif
|
|
}
|
|
|
|
// note CPUAFFINITY code used to reside here
|
|
void I_RegisterSysCommands(void) {}
|
|
#endif
|