2006-02-24 04:48:15 +00:00
|
|
|
/*
|
|
|
|
** c_console.cpp
|
|
|
|
** Implements the console itself
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
2006-06-11 01:37:00 +00:00
|
|
|
** Copyright 1998-2006 Randy Heit
|
2006-02-24 04:48:15 +00:00
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "m_alloc.h"
|
|
|
|
#include "templates.h"
|
2006-05-11 04:00:58 +00:00
|
|
|
#include "p_setup.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "version.h"
|
|
|
|
#include "g_game.h"
|
|
|
|
#include "c_console.h"
|
|
|
|
#include "c_cvars.h"
|
|
|
|
#include "c_dispatch.h"
|
|
|
|
#include "hu_stuff.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "i_video.h"
|
|
|
|
#include "i_input.h"
|
|
|
|
#include "m_swap.h"
|
|
|
|
#include "v_palette.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "v_text.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "r_main.h"
|
|
|
|
#include "r_draw.h"
|
|
|
|
#include "sbar.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "s_sndseq.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "d_gui.h"
|
|
|
|
#include "v_video.h"
|
2007-12-28 11:17:52 +00:00
|
|
|
#include "vectors.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#include "gi.h"
|
|
|
|
|
|
|
|
#define CONSOLESIZE 16384 // Number of characters to store in console
|
|
|
|
#define CONSOLELINES 256 // Max number of lines of console text
|
|
|
|
#define LINEMASK (CONSOLELINES-1)
|
|
|
|
|
|
|
|
#define LEFTMARGIN 8
|
|
|
|
#define RIGHTMARGIN 8
|
|
|
|
#define BOTTOMARGIN 12
|
|
|
|
|
|
|
|
static void C_TabComplete (bool goForward);
|
|
|
|
static bool C_TabCompleteList ();
|
|
|
|
static bool TabbedLast; // True if last key pressed was tab
|
|
|
|
static bool TabbedList; // True if tab list was shown
|
|
|
|
CVAR (Bool, con_notablist, false, CVAR_ARCHIVE)
|
|
|
|
|
|
|
|
static int conback;
|
- Discovered that Shader Model 1.4 clamps my constants, so I can't use
palettes smaller than 256 entries with the shader I wrote for it. Is there
a list of gotchas like this listed some where? I'd really like to see it.
Well, when compiled with SM2.0, the PalTex shader seems to be every-so-
slightly faster on my GF7950GT than the SM1.4 version, so I guess it's a
minor win for cards that support it.
- Fixed: ST_Endoom() failed to free the bitmap it used.
- Added the DTA_ColorOverlay attribute to blend a color with the texture
being drawn. For software, this (currently) only works with black. For
hardware, it works with any color. The motiviation for this was so I could
rewrite the status bar calls that passed DIM_MAP to DTA_Translation to
draw darker icons into something that didn't require making a whole new
remap table.
- After having an "OMG! How could I have been so stupid?" moment, I have
removed the off-by-one check from D3DFB. I had thought the off-by-one error
was caused by rounding errors by the shader hardware. Not so. Rather, I
wasn't sampling what I thought I was sampling. A texture that uses palette
index 255 passes the value 1.0 to the shader. The shader needs to adjust the
range of its palette indexes, or it will end up trying to read color 256
from the palette texture when it should be reading color 255. Doh!
- The TranslationToTable() function has been added to map from translation
numbers used by actors to the tables those numbers represent. This function
performs validation for the input and returns NULL if the input value
is invalid.
- Major changes to the way translation tables work: No longer are they each a
256-byte array. Instead, the FRemapTable structure is used to represent each
one. It includes a remap array for the software renderer, a palette array
for a hardware renderer, and a native texture pointer for D3DFB. The
translationtables array itself is now an array of TArrays that point to the
real tables. The DTA_Translation attribute must also be passed a pointer
to a FRemapTable, not a byte array as previously.
- Modified DFrameBuffer::DrawRateStuff() so that it can do its thing properly
for D3DFB's 2D mode. Before, any fullscreen graphics (like help images)
covered it up.
SVN r640 (trunk)
2007-12-26 04:42:15 +00:00
|
|
|
static DWORD conshade;
|
2006-02-24 04:48:15 +00:00
|
|
|
static bool conline;
|
|
|
|
|
|
|
|
extern int gametic;
|
|
|
|
extern bool automapactive; // in AM_map.c
|
2006-09-14 00:02:31 +00:00
|
|
|
extern bool advancedemo;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-05-12 03:14:40 +00:00
|
|
|
extern FBaseCVar *CVars;
|
|
|
|
extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE];
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
int ConCols, PhysRows;
|
2006-09-14 00:02:31 +00:00
|
|
|
bool vidactive = false, gotconback = false;
|
|
|
|
bool cursoron = false;
|
2006-02-24 04:48:15 +00:00
|
|
|
int ConBottom, ConScroll, RowAdjust;
|
|
|
|
int CursorTicker;
|
|
|
|
constate_e ConsoleState = c_up;
|
|
|
|
|
|
|
|
static char ConsoleBuffer[CONSOLESIZE];
|
|
|
|
static char *Lines[CONSOLELINES];
|
|
|
|
static bool LineJoins[CONSOLELINES];
|
|
|
|
|
|
|
|
static int TopLine, InsertLine;
|
|
|
|
static char *BufferRover = ConsoleBuffer;
|
|
|
|
|
|
|
|
static void ClearConsole ();
|
|
|
|
|
2006-05-12 03:14:40 +00:00
|
|
|
struct GameAtExit
|
|
|
|
{
|
|
|
|
GameAtExit *Next;
|
|
|
|
char Command[1];
|
|
|
|
};
|
|
|
|
|
|
|
|
static GameAtExit *ExitCmdList;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#define SCROLLUP 1
|
|
|
|
#define SCROLLDN 2
|
|
|
|
#define SCROLLNO 0
|
|
|
|
|
|
|
|
EXTERN_CVAR (Bool, show_messages)
|
|
|
|
|
|
|
|
static unsigned int TickerAt, TickerMax;
|
- Added some hackery at the start of MouseRead_Win32() that prevents it from
yanking the mouse around if they keys haven't been read yet to combat the
same situation that causes the keyboard to return DIERR_NOTACQUIRED in
KeyRead(): The window is sort of in focus and sort of not. User.dll
considers it to be focused and it's drawn as such, but another focused
window is on top of it, and DirectInput doesn't see it as focused.
- Fixed: KeyRead() should handle DIERR_NOTACQUIRED errors the same way it
handles DIERR_INPUTLOST errors. This can happen if our window had the
focus stolen away from it before we tried to acquire the keyboard in
DI_Init2(). Strangely, MouseRead_DI() already did this.
- When a stack overflow occurs, report.txt now only includes the first and
last 16KB of the stack to make it more manageable.
- Limited StreamEditBinary() to the first 64KB of the file to keep it from
taking too long on large dumps.
- And now I know why gathering crash information in the same process that
crashed can be bad: Stack overflows. You get one spare page to play with
when the stack overflows. MiniDumpWriteDump() needs more than that and
causes an access violation when it runs out of leftover stack, silently
terminating the application. Windows XP x64 offers SetThreadStackGuarantee()
to increase this, but that isn't available on anything older, including
32-bit XP. To get around this, a new thread is created to write the mini
dump when the stack overflows.
- Changed A_Burnination() to be closer to Strife's.
- Fixed: When playing back demos, DoAddBot() can be called without an
associated call to SpawnBot(). So if the bot can't spawn, botnum can
go negative, which will cause problems later in DCajunMaster::Main()
when it sees that wanted_botnum (0) is higher than botnum (-1).
- Fixed: Stopping demo recording in multiplayer games should not abruptly
drop the recorder out of the game without notifying the other players.
In fact, there's no reason why it should drop them out of multiplayer at
all.
- Fixed: Earthquakes were unreliable in multiplayer games because
P_PredictPlayer() did not preserve the player's xviewshift.
- Fixed: PlayerIsGone() needs to stop any scripts that belong to the player
who left, in addition to executing disconnect scripts.
- Fixed: APlayerPawn::AddInventory() should also check for a NULL player->mo
in case the player left but somebody still has a reference to their actor.
- Fixed: DDrawFB::PaintToWindow() should simulate proper unlocking behavior
and set Buffer to NULL.
- Improved feedback for network game initialization with the console ticker.
- Moved i_net.cpp and i_net.h out of sdl/ and win32/ and into the main source
directory. They are identical, so keeping two copies of them is bad.
- Fixed: (At least with Creative's driver's,) EAX settings are global and not
per-application. So if you play a multiplayer ZDoom game on one computer
(or even another EAX-using application), ZDoom needs to restore the
environment when it regains focus.
- Maybe fixed: (See http://forum.zdoom.org/potato.php?t=10689) Apparently,
PacketGet can receive ECONNRESET from nodes that aren't in the game. It
should be safe to just ignore these packets.
- Fixed: PlayerIsGone() should set the gone player's camera to NULL in case
the player who left was player 0. This is because if a remaining player
receives a "recoverable" error, they will become player 0. Once that happens,
they game will try to update sounds through their camera and crash in
FMODSoundRenderer::UpdateListener() because the zones array is now NULL.
G_NewInit() should also clear all the player structures.
SVN r233 (trunk)
2006-06-30 02:13:26 +00:00
|
|
|
static bool TickerPercent;
|
2006-02-24 04:48:15 +00:00
|
|
|
static const char *TickerLabel;
|
|
|
|
|
|
|
|
static bool TickerVisible;
|
|
|
|
static bool ConsoleDrawing;
|
|
|
|
|
2006-05-12 03:14:40 +00:00
|
|
|
// Buffer for AddToConsole()
|
|
|
|
static char *work = NULL;
|
|
|
|
static int worklen = 0;
|
|
|
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
struct History
|
|
|
|
{
|
|
|
|
struct History *Older;
|
|
|
|
struct History *Newer;
|
|
|
|
char String[1];
|
|
|
|
};
|
|
|
|
|
|
|
|
// CmdLine[0] = # of chars on command line
|
|
|
|
// CmdLine[1] = cursor position
|
|
|
|
// CmdLine[2+] = command line (max 255 chars + NULL)
|
|
|
|
// CmdLine[259]= offset from beginning of cmdline to display
|
2006-09-14 00:02:31 +00:00
|
|
|
static BYTE CmdLine[260];
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#define MAXHISTSIZE 50
|
|
|
|
static struct History *HistHead = NULL, *HistTail = NULL, *HistPos = NULL;
|
|
|
|
static int HistSize;
|
|
|
|
|
|
|
|
CVAR (Float, con_notifytime, 3.f, CVAR_ARCHIVE)
|
|
|
|
CVAR (Bool, con_centernotify, false, CVAR_ARCHIVE)
|
|
|
|
CUSTOM_CVAR (Int, con_scaletext, 0, CVAR_ARCHIVE) // Scale notify text at high resolutions?
|
|
|
|
{
|
|
|
|
if (self < 0) self = 0;
|
|
|
|
if (self > 2) self = 2;
|
|
|
|
}
|
|
|
|
|
2007-12-28 11:17:52 +00:00
|
|
|
CUSTOM_CVAR(Float, con_alpha, 0.75f, CVAR_ARCHIVE)
|
|
|
|
{
|
|
|
|
if (self < 0.f) self = 0.f;
|
|
|
|
if (self > 1.f) self = 1.f;
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// Command to run when Ctrl-D is pressed at start of line
|
|
|
|
CVAR (String, con_ctrl_d, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
|
|
|
|
#define NUMNOTIFIES 4
|
|
|
|
#define NOTIFYFADETIME 6
|
|
|
|
|
|
|
|
static struct NotifyText
|
|
|
|
{
|
2006-08-31 00:16:12 +00:00
|
|
|
int TimeOut;
|
|
|
|
int PrintLevel;
|
|
|
|
FString Text;
|
2006-02-24 04:48:15 +00:00
|
|
|
} NotifyStrings[NUMNOTIFIES];
|
|
|
|
|
|
|
|
static int NotifyTop, NotifyTopGoal;
|
|
|
|
|
|
|
|
#define PRINTLEVELS 5
|
|
|
|
int PrintColors[PRINTLEVELS+2] = { CR_RED, CR_GOLD, CR_GRAY, CR_GREEN, CR_GREEN, CR_GOLD };
|
|
|
|
|
|
|
|
static void setmsgcolor (int index, int color);
|
|
|
|
|
|
|
|
FILE *Logfile = NULL;
|
|
|
|
|
|
|
|
void C_AddNotifyString (int printlevel, const char *source);
|
|
|
|
|
|
|
|
|
|
|
|
FIntCVar msglevel ("msg", 0, CVAR_ARCHIVE);
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, msg0color, 6, CVAR_ARCHIVE)
|
|
|
|
{
|
|
|
|
setmsgcolor (0, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, msg1color, 5, CVAR_ARCHIVE)
|
|
|
|
{
|
|
|
|
setmsgcolor (1, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, msg2color, 2, CVAR_ARCHIVE)
|
|
|
|
{
|
|
|
|
setmsgcolor (2, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, msg3color, 3, CVAR_ARCHIVE)
|
|
|
|
{
|
|
|
|
setmsgcolor (3, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, msg4color, 3, CVAR_ARCHIVE)
|
|
|
|
{
|
|
|
|
setmsgcolor (4, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, msgmidcolor, 5, CVAR_ARCHIVE)
|
|
|
|
{
|
|
|
|
setmsgcolor (PRINTLEVELS, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Int, msgmidcolor2, 4, CVAR_ARCHIVE)
|
|
|
|
{
|
|
|
|
setmsgcolor (PRINTLEVELS+1, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void maybedrawnow (bool tick, bool force)
|
|
|
|
{
|
2008-01-01 03:07:05 +00:00
|
|
|
// FIXME: Does not work right with hw2d
|
2008-03-12 02:56:11 +00:00
|
|
|
if (ConsoleDrawing || !gotconback || screen == NULL || screen->IsLocked () || screen->Accel2D)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vidactive &&
|
|
|
|
(((tick || gameaction != ga_nothing) && ConsoleState == c_down)
|
|
|
|
|| gamestate == GS_STARTUP))
|
|
|
|
{
|
|
|
|
static size_t lastprinttime = 0;
|
|
|
|
size_t nowtime = I_GetTime(false);
|
|
|
|
|
|
|
|
if (nowtime - lastprinttime > 1 || force)
|
|
|
|
{
|
|
|
|
screen->Lock (false);
|
2007-12-27 04:30:12 +00:00
|
|
|
C_DrawConsole (false);
|
2006-02-24 04:48:15 +00:00
|
|
|
screen->Update ();
|
|
|
|
lastprinttime = nowtime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TextQueue
|
|
|
|
{
|
|
|
|
TextQueue (bool notify, int printlevel, const char *text)
|
|
|
|
: Next(NULL), bNotify(notify), PrintLevel(printlevel), Text(text)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
TextQueue *Next;
|
|
|
|
bool bNotify;
|
|
|
|
int PrintLevel;
|
2006-05-03 22:45:01 +00:00
|
|
|
FString Text;
|
2006-02-24 04:48:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TextQueue *EnqueuedText, **EnqueuedTextTail = &EnqueuedText;
|
|
|
|
|
|
|
|
void EnqueueConsoleText (bool notify, int printlevel, const char *text)
|
|
|
|
{
|
|
|
|
TextQueue *queued = new TextQueue (notify, printlevel, text);
|
|
|
|
*EnqueuedTextTail = queued;
|
|
|
|
EnqueuedTextTail = &queued->Next;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DequeueConsoleText ()
|
|
|
|
{
|
|
|
|
TextQueue *queued = EnqueuedText;
|
|
|
|
|
|
|
|
while (queued != NULL)
|
|
|
|
{
|
|
|
|
TextQueue *next = queued->Next;
|
|
|
|
if (queued->bNotify)
|
|
|
|
{
|
2006-05-03 22:45:01 +00:00
|
|
|
C_AddNotifyString (queued->PrintLevel, queued->Text);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-05-03 22:45:01 +00:00
|
|
|
AddToConsole (queued->PrintLevel, queued->Text);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
delete queued;
|
|
|
|
queued = next;
|
|
|
|
}
|
|
|
|
EnqueuedText = NULL;
|
|
|
|
EnqueuedTextTail = &EnqueuedText;
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
void C_InitConsole (int width, int height, bool ingame)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if ( (vidactive = ingame) )
|
|
|
|
{
|
|
|
|
if (!gotconback)
|
|
|
|
{
|
|
|
|
conback = TexMan.CheckForTexture ("CONBACK", FTexture::TEX_MiscPatch);
|
|
|
|
|
|
|
|
if (conback <= 0)
|
|
|
|
{
|
|
|
|
conback = TexMan.GetTexture (gameinfo.titlePage, FTexture::TEX_MiscPatch);
|
2008-01-02 05:21:48 +00:00
|
|
|
conshade = MAKEARGB(175,0,0,0);
|
2006-02-24 04:48:15 +00:00
|
|
|
conline = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-12-23 14:13:29 +00:00
|
|
|
conshade = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
conline = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
gotconback = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int cwidth, cheight;
|
|
|
|
|
|
|
|
if (ConFont != NULL)
|
|
|
|
{
|
|
|
|
cwidth = ConFont->GetCharWidth ('M');
|
|
|
|
cheight = ConFont->GetHeight();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cwidth = cheight = 8;
|
|
|
|
}
|
|
|
|
ConCols = (width - LEFTMARGIN - RIGHTMARGIN) / cwidth;
|
|
|
|
PhysRows = height / cheight;
|
|
|
|
|
|
|
|
// If there is some text in the console buffer, reformat it
|
|
|
|
// for the new resolution.
|
|
|
|
if (TopLine != InsertLine)
|
|
|
|
{
|
|
|
|
// Note: Don't use new here, because we attach a handler to new in
|
|
|
|
// i_main.cpp that calls I_FatalError if the allocation fails,
|
|
|
|
// but we can gracefully handle such a condition here by just
|
|
|
|
// clearing the console buffer. (OTOH, what are the chances that
|
|
|
|
// any other memory allocations would succeed if we can't get
|
|
|
|
// these mallocs here?)
|
|
|
|
|
|
|
|
char *fmtBuff = (char *)malloc (CONSOLESIZE);
|
|
|
|
char **fmtLines = (char **)malloc (CONSOLELINES*sizeof(char*)*4);
|
|
|
|
int out = 0;
|
|
|
|
|
|
|
|
if (fmtBuff && fmtLines)
|
|
|
|
{
|
|
|
|
int in;
|
|
|
|
char *fmtpos;
|
|
|
|
bool newline = true;
|
|
|
|
|
|
|
|
fmtpos = fmtBuff;
|
|
|
|
memset (fmtBuff, 0, CONSOLESIZE);
|
|
|
|
|
|
|
|
for (in = TopLine; in != InsertLine; in = (in + 1) & LINEMASK)
|
|
|
|
{
|
|
|
|
size_t len = strlen (Lines[in]);
|
|
|
|
|
|
|
|
if (fmtpos + len + 2 - fmtBuff > CONSOLESIZE)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (newline)
|
|
|
|
{
|
|
|
|
newline = false;
|
|
|
|
fmtLines[out++] = fmtpos;
|
|
|
|
}
|
|
|
|
strcpy (fmtpos, Lines[in]);
|
|
|
|
fmtpos += len;
|
|
|
|
if (!LineJoins[in])
|
|
|
|
{
|
|
|
|
*fmtpos++ = '\n';
|
|
|
|
fmtpos++;
|
|
|
|
if (out == CONSOLELINES*4)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
newline = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ClearConsole ();
|
|
|
|
|
|
|
|
if (fmtBuff && fmtLines)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < out; i++)
|
|
|
|
{
|
|
|
|
AddToConsole (-1, fmtLines[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmtBuff)
|
|
|
|
free (fmtBuff);
|
|
|
|
if (fmtLines)
|
|
|
|
free (fmtLines);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-12 03:14:40 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// CCMD atexit
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
CCMD (atexit)
|
|
|
|
{
|
|
|
|
if (argv.argc() == 1)
|
|
|
|
{
|
|
|
|
Printf ("Registered atexit commands:\n");
|
|
|
|
GameAtExit *record = ExitCmdList;
|
|
|
|
while (record != NULL)
|
|
|
|
{
|
|
|
|
Printf ("%s\n", record->Command);
|
|
|
|
record = record->Next;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int i = 1; i < argv.argc(); ++i)
|
|
|
|
{
|
|
|
|
GameAtExit *record = (GameAtExit *)M_Malloc (
|
|
|
|
sizeof(GameAtExit)+strlen(argv[i]));
|
|
|
|
strcpy (record->Command, argv[i]);
|
|
|
|
record->Next = ExitCmdList;
|
|
|
|
ExitCmdList = record;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// C_DeinitConsole
|
|
|
|
//
|
|
|
|
// Executes the contents of the atexit cvar, if any, at quit time.
|
|
|
|
// Then releases all of the console's memory.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void C_DeinitConsole ()
|
|
|
|
{
|
|
|
|
GameAtExit *cmd = ExitCmdList;
|
|
|
|
|
|
|
|
while (cmd != NULL)
|
|
|
|
{
|
|
|
|
GameAtExit *next = cmd->Next;
|
|
|
|
AddCommandString (cmd->Command);
|
2008-02-17 02:40:03 +00:00
|
|
|
M_Free (cmd);
|
2006-05-12 03:14:40 +00:00
|
|
|
cmd = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free command history
|
|
|
|
History *hist = HistTail;
|
|
|
|
|
|
|
|
while (hist != NULL)
|
|
|
|
{
|
|
|
|
History *next = hist->Newer;
|
|
|
|
free (hist);
|
|
|
|
hist = next;
|
|
|
|
}
|
|
|
|
HistTail = HistHead = HistPos = NULL;
|
|
|
|
|
|
|
|
// Free cvars allocated at runtime
|
2006-06-11 01:37:00 +00:00
|
|
|
FBaseCVar *var, *next, **nextp;
|
|
|
|
for (var = CVars, nextp = &CVars; var != NULL; var = next)
|
|
|
|
{
|
|
|
|
next = var->m_Next;
|
|
|
|
if (var->GetFlags() & CVAR_UNSETTABLE)
|
|
|
|
{
|
|
|
|
delete var;
|
|
|
|
*nextp = next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nextp = &var->m_Next;
|
|
|
|
}
|
|
|
|
}
|
2006-05-12 03:14:40 +00:00
|
|
|
|
|
|
|
// Free alias commands. (i.e. The "commands" that can be allocated
|
|
|
|
// at runtime.)
|
|
|
|
for (size_t i = 0; i < countof(Commands); ++i)
|
|
|
|
{
|
|
|
|
FConsoleCommand *cmd = Commands[i];
|
|
|
|
|
|
|
|
while (cmd != NULL)
|
|
|
|
{
|
|
|
|
FConsoleCommand *next = cmd->m_Next;
|
|
|
|
if (cmd->IsAlias())
|
|
|
|
{
|
|
|
|
delete cmd;
|
|
|
|
}
|
|
|
|
cmd = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure all tab commands are cleared before the memory for
|
|
|
|
// their names is deallocated.
|
|
|
|
C_ClearTabCommands ();
|
|
|
|
|
|
|
|
// Free AddToConsole()'s work buffer
|
|
|
|
if (work != NULL)
|
|
|
|
{
|
|
|
|
free (work);
|
|
|
|
work = NULL;
|
|
|
|
worklen = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
static void ClearConsole ()
|
|
|
|
{
|
|
|
|
RowAdjust = 0;
|
|
|
|
TopLine = InsertLine = 0;
|
|
|
|
BufferRover = ConsoleBuffer;
|
|
|
|
memset (ConsoleBuffer, 0, CONSOLESIZE);
|
|
|
|
memset (Lines, 0, sizeof(Lines));
|
|
|
|
memset (LineJoins, 0, sizeof(LineJoins));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setmsgcolor (int index, int color)
|
|
|
|
{
|
|
|
|
if ((unsigned)color >= (unsigned)NUM_TEXT_COLORS)
|
|
|
|
color = 0;
|
|
|
|
PrintColors[index] = color;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int DisplayWidth;
|
|
|
|
|
|
|
|
void C_AddNotifyString (int printlevel, const char *source)
|
|
|
|
{
|
|
|
|
static enum
|
|
|
|
{
|
|
|
|
NEWLINE,
|
|
|
|
APPENDLINE,
|
|
|
|
REPLACELINE
|
|
|
|
} addtype = NEWLINE;
|
|
|
|
|
2006-08-31 00:16:12 +00:00
|
|
|
FBrokenLines *lines;
|
2006-02-24 04:48:15 +00:00
|
|
|
int i, len, width;
|
|
|
|
|
|
|
|
if ((printlevel != 128 && !show_messages) ||
|
|
|
|
!(len = (int)strlen (source)) ||
|
|
|
|
gamestate == GS_FULLCONSOLE ||
|
|
|
|
gamestate == GS_DEMOSCREEN)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ConsoleDrawing)
|
|
|
|
{
|
|
|
|
EnqueueConsoleText (true, printlevel, source);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
width = con_scaletext > 1 ? DisplayWidth/2 : con_scaletext == 1 ? DisplayWidth / CleanXfac : DisplayWidth;
|
|
|
|
|
2006-08-31 00:16:12 +00:00
|
|
|
if (addtype == APPENDLINE && NotifyStrings[NUMNOTIFIES-1].PrintLevel == printlevel)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-08-31 00:16:12 +00:00
|
|
|
FString str = NotifyStrings[NUMNOTIFIES-1].Text + source;
|
2006-09-19 23:25:51 +00:00
|
|
|
lines = V_BreakLines (screen->Font, width, str);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-09-19 23:25:51 +00:00
|
|
|
lines = V_BreakLines (screen->Font, width, source);
|
2006-02-24 04:48:15 +00:00
|
|
|
addtype = (addtype == APPENDLINE) ? NEWLINE : addtype;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lines == NULL)
|
|
|
|
return;
|
|
|
|
|
2006-08-31 00:16:12 +00:00
|
|
|
for (i = 0; lines[i].Width >= 0; i++)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (addtype == NEWLINE)
|
2006-08-31 00:16:12 +00:00
|
|
|
{
|
|
|
|
for (int j = 0; j < NUMNOTIFIES-1; ++j)
|
|
|
|
{
|
|
|
|
NotifyStrings[j] = NotifyStrings[j+1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NotifyStrings[NUMNOTIFIES-1].Text = lines[i].Text;
|
|
|
|
NotifyStrings[NUMNOTIFIES-1].TimeOut = gametic + (int)(con_notifytime * TICRATE);
|
|
|
|
NotifyStrings[NUMNOTIFIES-1].PrintLevel = printlevel;
|
2006-02-24 04:48:15 +00:00
|
|
|
addtype = NEWLINE;
|
|
|
|
}
|
|
|
|
|
|
|
|
V_FreeBrokenLines (lines);
|
2006-08-31 00:16:12 +00:00
|
|
|
lines = NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
switch (source[len-1])
|
|
|
|
{
|
|
|
|
case '\r': addtype = REPLACELINE; break;
|
|
|
|
case '\n': addtype = NEWLINE; break;
|
|
|
|
default: addtype = APPENDLINE; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
NotifyTopGoal = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int FlushLines (const char *start, const char *stop)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = TopLine; i != InsertLine; i = (i + 1) & LINEMASK)
|
|
|
|
{
|
|
|
|
if (Lines[i] < stop && Lines[i] + strlen (Lines[i]) > start)
|
|
|
|
{
|
|
|
|
Lines[i] = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i != TopLine)
|
|
|
|
i = i;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddLine (const char *text, bool more, int len)
|
|
|
|
{
|
|
|
|
if (BufferRover + len + 1 - ConsoleBuffer > CONSOLESIZE)
|
|
|
|
{
|
|
|
|
TopLine = FlushLines (BufferRover, ConsoleBuffer + CONSOLESIZE);
|
|
|
|
BufferRover = ConsoleBuffer;
|
|
|
|
}
|
2007-01-23 01:13:17 +00:00
|
|
|
if (len >= CONSOLESIZE - 1)
|
|
|
|
{
|
|
|
|
text = text + len - CONSOLESIZE + 1;
|
|
|
|
len = CONSOLESIZE - 1;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
TopLine = FlushLines (BufferRover, BufferRover + len + 1);
|
|
|
|
memcpy (BufferRover, text, len);
|
|
|
|
BufferRover[len] = 0;
|
|
|
|
Lines[InsertLine] = BufferRover;
|
|
|
|
BufferRover += len + 1;
|
|
|
|
LineJoins[InsertLine] = more;
|
|
|
|
InsertLine = (InsertLine + 1) & LINEMASK;
|
|
|
|
if (InsertLine == TopLine)
|
|
|
|
{
|
|
|
|
TopLine = (TopLine + 1) & LINEMASK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddToConsole (int printlevel, const char *text)
|
|
|
|
{
|
|
|
|
static enum
|
|
|
|
{
|
|
|
|
NEWLINE,
|
|
|
|
APPENDLINE,
|
|
|
|
REPLACELINE
|
|
|
|
} addtype = NEWLINE;
|
|
|
|
|
|
|
|
char *work_p;
|
|
|
|
char *linestart;
|
2006-08-31 01:30:22 +00:00
|
|
|
FString cc('A' + char(CR_TAN));
|
2006-02-24 04:48:15 +00:00
|
|
|
int size, len;
|
|
|
|
int x;
|
|
|
|
int maxwidth;
|
|
|
|
|
|
|
|
if (ConsoleDrawing)
|
|
|
|
{
|
|
|
|
EnqueueConsoleText (false, printlevel, text);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = (int)strlen (text);
|
|
|
|
size = len + 3;
|
|
|
|
|
|
|
|
if (addtype != NEWLINE)
|
|
|
|
{
|
|
|
|
InsertLine = (InsertLine - 1) & LINEMASK;
|
|
|
|
if (Lines[InsertLine] == NULL)
|
|
|
|
{
|
|
|
|
InsertLine = (InsertLine + 1) & LINEMASK;
|
|
|
|
addtype = NEWLINE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BufferRover = Lines[InsertLine];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (addtype == APPENDLINE)
|
|
|
|
{
|
|
|
|
size += (int)strlen (Lines[InsertLine]);
|
|
|
|
}
|
|
|
|
if (size > worklen)
|
|
|
|
{
|
2006-05-04 03:49:46 +00:00
|
|
|
work = (char *)M_Realloc (work, size);
|
2006-02-24 04:48:15 +00:00
|
|
|
worklen = size;
|
|
|
|
}
|
|
|
|
if (work == NULL)
|
2007-01-02 09:51:04 +00:00
|
|
|
{
|
2006-12-29 03:38:37 +00:00
|
|
|
static char oom[] = TEXTCOLOR_RED "*** OUT OF MEMORY ***";
|
|
|
|
work = oom;
|
2006-02-24 04:48:15 +00:00
|
|
|
worklen = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (addtype == APPENDLINE)
|
|
|
|
{
|
|
|
|
strcpy (work, Lines[InsertLine]);
|
|
|
|
strcat (work, text);
|
|
|
|
}
|
|
|
|
else if (printlevel >= 0)
|
|
|
|
{
|
|
|
|
work[0] = TEXTCOLOR_ESCAPE;
|
|
|
|
work[1] = 'A' + (printlevel == PRINT_HIGH ? CR_TAN :
|
|
|
|
printlevel == 200 ? CR_GREEN :
|
|
|
|
printlevel < PRINTLEVELS ? PrintColors[printlevel] :
|
|
|
|
CR_TAN);
|
|
|
|
cc = work[1];
|
|
|
|
strcpy (work + 2, text);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
strcpy (work, text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
work_p = linestart = work;
|
|
|
|
|
|
|
|
if (ConFont != NULL && screen != NULL)
|
|
|
|
{
|
|
|
|
x = 0;
|
|
|
|
maxwidth = screen->GetWidth() - LEFTMARGIN - RIGHTMARGIN;
|
|
|
|
|
|
|
|
while (*work_p)
|
|
|
|
{
|
|
|
|
if (*work_p == TEXTCOLOR_ESCAPE)
|
|
|
|
{
|
|
|
|
work_p++;
|
2006-08-31 00:16:12 +00:00
|
|
|
if (*work_p == '[')
|
|
|
|
{
|
|
|
|
char *start = work_p;
|
|
|
|
while (*work_p != ']' && *work_p != '\0')
|
|
|
|
{
|
|
|
|
work_p++;
|
|
|
|
}
|
|
|
|
if (*work_p != '\0')
|
|
|
|
{
|
|
|
|
work_p++;
|
|
|
|
}
|
|
|
|
cc = FString(start, work_p - start);
|
|
|
|
}
|
|
|
|
else if (*work_p != '\0')
|
|
|
|
{
|
2006-02-24 04:48:15 +00:00
|
|
|
cc = *work_p++;
|
2006-08-31 00:16:12 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int w = ConFont->GetCharWidth (*work_p);
|
|
|
|
if (*work_p == '\n' || x + w > maxwidth)
|
|
|
|
{
|
|
|
|
AddLine (linestart, *work_p != '\n', work_p - linestart);
|
|
|
|
if (*work_p == '\n')
|
|
|
|
{
|
|
|
|
x = 0;
|
|
|
|
work_p++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = w;
|
|
|
|
}
|
|
|
|
if (*work_p)
|
|
|
|
{
|
2006-08-31 00:16:12 +00:00
|
|
|
linestart = work_p - 1 - cc.Len();
|
2006-02-24 04:48:15 +00:00
|
|
|
linestart[0] = TEXTCOLOR_ESCAPE;
|
2006-08-31 00:16:12 +00:00
|
|
|
strncpy (linestart + 1, cc, cc.Len());
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
linestart = work_p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x += w;
|
|
|
|
work_p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*linestart)
|
|
|
|
{
|
|
|
|
AddLine (linestart, true, work_p - linestart);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (*work_p)
|
|
|
|
{
|
|
|
|
if (*work_p++ == '\n')
|
|
|
|
{
|
|
|
|
AddLine (linestart, false, work_p - linestart - 1);
|
|
|
|
linestart = work_p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*linestart)
|
|
|
|
{
|
|
|
|
AddLine (linestart, true, work_p - linestart);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (text[len-1])
|
|
|
|
{
|
|
|
|
case '\r': addtype = REPLACELINE; break;
|
|
|
|
case '\n': addtype = NEWLINE; break;
|
|
|
|
default: addtype = APPENDLINE; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Adds a string to the console and also to the notify buffer */
|
|
|
|
int PrintString (int printlevel, const char *outline)
|
|
|
|
{
|
|
|
|
if (printlevel < msglevel || *outline == '\0')
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Logfile)
|
|
|
|
{
|
2006-06-21 15:40:42 +00:00
|
|
|
// Strip out any color escape sequences before writing to the log file
|
|
|
|
char * copy = new char[strlen(outline)+1];
|
|
|
|
const char * srcp = outline;
|
|
|
|
char * dstp = copy;
|
|
|
|
|
|
|
|
while (*srcp != 0)
|
|
|
|
{
|
|
|
|
if (*srcp!=0x1c)
|
|
|
|
{
|
|
|
|
*dstp++=*srcp++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (srcp[1]!=0) srcp+=2;
|
|
|
|
else break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*dstp=0;
|
|
|
|
|
|
|
|
fputs (copy, Logfile);
|
|
|
|
delete [] copy;
|
2006-02-24 04:48:15 +00:00
|
|
|
//#ifdef _DEBUG
|
|
|
|
fflush (Logfile);
|
|
|
|
//#endif
|
|
|
|
}
|
|
|
|
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
I_PrintStr (outline);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
AddToConsole (printlevel, outline);
|
|
|
|
if (vidactive && screen && screen->Font)
|
|
|
|
{
|
|
|
|
C_AddNotifyString (printlevel, outline);
|
|
|
|
maybedrawnow (false, false);
|
|
|
|
}
|
|
|
|
return (int)strlen (outline);
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
extern bool gameisdead;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
int VPrintf (int printlevel, const char *format, va_list parms)
|
|
|
|
{
|
|
|
|
if (gameisdead)
|
|
|
|
return 0;
|
|
|
|
|
2006-05-03 22:45:01 +00:00
|
|
|
FString outline;
|
2006-04-18 06:07:09 +00:00
|
|
|
outline.VFormat (format, parms);
|
2008-03-26 08:50:54 +00:00
|
|
|
return PrintString (printlevel, outline.GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int STACK_ARGS Printf (int printlevel, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list argptr;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
va_start (argptr, format);
|
|
|
|
count = VPrintf (printlevel, format, argptr);
|
|
|
|
va_end (argptr);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
int STACK_ARGS Printf (const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list argptr;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
va_start (argptr, format);
|
|
|
|
count = VPrintf (PRINT_HIGH, format, argptr);
|
|
|
|
va_end (argptr);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
int STACK_ARGS DPrintf (const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list argptr;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
if (developer)
|
|
|
|
{
|
|
|
|
va_start (argptr, format);
|
|
|
|
count = VPrintf (PRINT_HIGH, format, argptr);
|
|
|
|
va_end (argptr);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_FlushDisplay ()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NUMNOTIFIES; i++)
|
2006-08-31 00:16:12 +00:00
|
|
|
NotifyStrings[i].TimeOut = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void C_AdjustBottom ()
|
|
|
|
{
|
|
|
|
if (gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP)
|
|
|
|
ConBottom = SCREENHEIGHT;
|
|
|
|
else if (ConBottom > SCREENHEIGHT / 2 || ConsoleState == c_down)
|
|
|
|
ConBottom = SCREENHEIGHT / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_NewModeAdjust ()
|
|
|
|
{
|
|
|
|
C_InitConsole (SCREENWIDTH, SCREENHEIGHT, true);
|
|
|
|
C_FlushDisplay ();
|
|
|
|
C_AdjustBottom ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_Ticker ()
|
|
|
|
{
|
|
|
|
static int lasttic = 0;
|
|
|
|
|
|
|
|
if (lasttic == 0)
|
|
|
|
lasttic = gametic - 1;
|
|
|
|
|
|
|
|
if (ConsoleState != c_up)
|
|
|
|
{
|
|
|
|
if (ConsoleState == c_falling)
|
|
|
|
{
|
|
|
|
ConBottom += (gametic - lasttic) * (SCREENHEIGHT*2/25);
|
|
|
|
if (ConBottom >= SCREENHEIGHT / 2)
|
|
|
|
{
|
|
|
|
ConBottom = SCREENHEIGHT / 2;
|
|
|
|
ConsoleState = c_down;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ConsoleState == c_rising)
|
|
|
|
{
|
|
|
|
ConBottom -= (gametic - lasttic) * (SCREENHEIGHT*2/25);
|
|
|
|
if (ConBottom <= 0)
|
|
|
|
{
|
|
|
|
ConsoleState = c_up;
|
|
|
|
ConBottom = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (--CursorTicker <= 0)
|
|
|
|
{
|
|
|
|
cursoron ^= 1;
|
|
|
|
CursorTicker = C_BLINKRATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
lasttic = gametic;
|
|
|
|
|
|
|
|
if (NotifyTopGoal > NotifyTop)
|
|
|
|
{
|
|
|
|
NotifyTop++;
|
|
|
|
}
|
|
|
|
else if (NotifyTopGoal < NotifyTop)
|
|
|
|
{
|
|
|
|
NotifyTop--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void C_DrawNotifyText ()
|
|
|
|
{
|
|
|
|
bool center = (con_centernotify != 0.f);
|
|
|
|
int i, line, lineadv, color, j, skip;
|
|
|
|
bool canskip;
|
|
|
|
|
|
|
|
if (gamestate == GS_FULLCONSOLE || gamestate == GS_DEMOSCREEN/* || menuactive != MENU_Off*/)
|
|
|
|
return;
|
|
|
|
|
|
|
|
line = NotifyTop;
|
|
|
|
skip = 0;
|
|
|
|
canskip = true;
|
|
|
|
|
|
|
|
lineadv = SmallFont->GetHeight ();
|
|
|
|
if (con_scaletext == 1)
|
|
|
|
{
|
|
|
|
lineadv *= CleanYfac;
|
|
|
|
}
|
|
|
|
|
|
|
|
BorderTopRefresh = screen->GetPageCount ();
|
|
|
|
|
|
|
|
for (i = 0; i < NUMNOTIFIES; i++)
|
|
|
|
{
|
2006-08-31 00:16:12 +00:00
|
|
|
if (NotifyStrings[i].TimeOut == 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
continue;
|
|
|
|
|
2006-08-31 00:16:12 +00:00
|
|
|
j = NotifyStrings[i].TimeOut - gametic;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (j > 0)
|
|
|
|
{
|
2006-08-31 00:16:12 +00:00
|
|
|
if (!show_messages && NotifyStrings[i].PrintLevel != 128)
|
2006-02-24 04:48:15 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
fixed_t alpha;
|
|
|
|
|
|
|
|
if (j < NOTIFYFADETIME)
|
|
|
|
{
|
|
|
|
alpha = OPAQUE * j / NOTIFYFADETIME;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
alpha = OPAQUE;
|
|
|
|
}
|
|
|
|
|
2006-08-31 00:16:12 +00:00
|
|
|
if (NotifyStrings[i].PrintLevel >= PRINTLEVELS)
|
2006-02-24 04:48:15 +00:00
|
|
|
color = CR_UNTRANSLATED;
|
|
|
|
else
|
2006-08-31 00:16:12 +00:00
|
|
|
color = PrintColors[NotifyStrings[i].PrintLevel];
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (con_scaletext == 1)
|
|
|
|
{
|
|
|
|
if (!center)
|
2006-08-31 00:16:12 +00:00
|
|
|
screen->DrawText (color, 0, line, NotifyStrings[i].Text,
|
|
|
|
DTA_CleanNoMove, true, DTA_Alpha, alpha, TAG_DONE);
|
2006-02-24 04:48:15 +00:00
|
|
|
else
|
|
|
|
screen->DrawText (color, (SCREENWIDTH -
|
2006-08-31 00:16:12 +00:00
|
|
|
SmallFont->StringWidth (NotifyStrings[i].Text)*CleanXfac)/2,
|
|
|
|
line, NotifyStrings[i].Text, DTA_CleanNoMove, true,
|
2006-02-24 04:48:15 +00:00
|
|
|
DTA_Alpha, alpha, TAG_DONE);
|
|
|
|
}
|
|
|
|
else if (con_scaletext == 0)
|
|
|
|
{
|
|
|
|
if (!center)
|
2006-08-31 00:16:12 +00:00
|
|
|
screen->DrawText (color, 0, line, NotifyStrings[i].Text,
|
2006-02-24 04:48:15 +00:00
|
|
|
DTA_Alpha, alpha, TAG_DONE);
|
|
|
|
else
|
|
|
|
screen->DrawText (color, (SCREENWIDTH -
|
2006-08-31 00:16:12 +00:00
|
|
|
SmallFont->StringWidth (NotifyStrings[i].Text))/2,
|
|
|
|
line, NotifyStrings[i].Text,
|
2006-02-24 04:48:15 +00:00
|
|
|
DTA_Alpha, alpha, TAG_DONE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!center)
|
2006-08-31 00:16:12 +00:00
|
|
|
screen->DrawText (color, 0, line, NotifyStrings[i].Text,
|
2006-02-24 04:48:15 +00:00
|
|
|
DTA_VirtualWidth, screen->GetWidth() / 2,
|
|
|
|
DTA_VirtualHeight, screen->GetHeight() / 2,
|
|
|
|
DTA_KeepRatio, true,
|
|
|
|
DTA_Alpha, alpha, TAG_DONE);
|
|
|
|
else
|
|
|
|
screen->DrawText (color, (screen->GetWidth() / 2 -
|
2006-08-31 00:16:12 +00:00
|
|
|
SmallFont->StringWidth (NotifyStrings[i].Text))/2,
|
|
|
|
line, NotifyStrings[i].Text,
|
2006-02-24 04:48:15 +00:00
|
|
|
DTA_VirtualWidth, screen->GetWidth() / 2,
|
|
|
|
DTA_VirtualHeight, screen->GetHeight() / 2,
|
|
|
|
DTA_KeepRatio, true,
|
|
|
|
DTA_Alpha, alpha, TAG_DONE);
|
|
|
|
}
|
|
|
|
line += lineadv;
|
|
|
|
canskip = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (canskip)
|
|
|
|
{
|
|
|
|
NotifyTop += lineadv;
|
|
|
|
line += lineadv;
|
|
|
|
skip++;
|
|
|
|
}
|
2006-08-31 00:16:12 +00:00
|
|
|
NotifyStrings[i].TimeOut = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (canskip)
|
|
|
|
{
|
|
|
|
NotifyTop = NotifyTopGoal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
- Added some hackery at the start of MouseRead_Win32() that prevents it from
yanking the mouse around if they keys haven't been read yet to combat the
same situation that causes the keyboard to return DIERR_NOTACQUIRED in
KeyRead(): The window is sort of in focus and sort of not. User.dll
considers it to be focused and it's drawn as such, but another focused
window is on top of it, and DirectInput doesn't see it as focused.
- Fixed: KeyRead() should handle DIERR_NOTACQUIRED errors the same way it
handles DIERR_INPUTLOST errors. This can happen if our window had the
focus stolen away from it before we tried to acquire the keyboard in
DI_Init2(). Strangely, MouseRead_DI() already did this.
- When a stack overflow occurs, report.txt now only includes the first and
last 16KB of the stack to make it more manageable.
- Limited StreamEditBinary() to the first 64KB of the file to keep it from
taking too long on large dumps.
- And now I know why gathering crash information in the same process that
crashed can be bad: Stack overflows. You get one spare page to play with
when the stack overflows. MiniDumpWriteDump() needs more than that and
causes an access violation when it runs out of leftover stack, silently
terminating the application. Windows XP x64 offers SetThreadStackGuarantee()
to increase this, but that isn't available on anything older, including
32-bit XP. To get around this, a new thread is created to write the mini
dump when the stack overflows.
- Changed A_Burnination() to be closer to Strife's.
- Fixed: When playing back demos, DoAddBot() can be called without an
associated call to SpawnBot(). So if the bot can't spawn, botnum can
go negative, which will cause problems later in DCajunMaster::Main()
when it sees that wanted_botnum (0) is higher than botnum (-1).
- Fixed: Stopping demo recording in multiplayer games should not abruptly
drop the recorder out of the game without notifying the other players.
In fact, there's no reason why it should drop them out of multiplayer at
all.
- Fixed: Earthquakes were unreliable in multiplayer games because
P_PredictPlayer() did not preserve the player's xviewshift.
- Fixed: PlayerIsGone() needs to stop any scripts that belong to the player
who left, in addition to executing disconnect scripts.
- Fixed: APlayerPawn::AddInventory() should also check for a NULL player->mo
in case the player left but somebody still has a reference to their actor.
- Fixed: DDrawFB::PaintToWindow() should simulate proper unlocking behavior
and set Buffer to NULL.
- Improved feedback for network game initialization with the console ticker.
- Moved i_net.cpp and i_net.h out of sdl/ and win32/ and into the main source
directory. They are identical, so keeping two copies of them is bad.
- Fixed: (At least with Creative's driver's,) EAX settings are global and not
per-application. So if you play a multiplayer ZDoom game on one computer
(or even another EAX-using application), ZDoom needs to restore the
environment when it regains focus.
- Maybe fixed: (See http://forum.zdoom.org/potato.php?t=10689) Apparently,
PacketGet can receive ECONNRESET from nodes that aren't in the game. It
should be safe to just ignore these packets.
- Fixed: PlayerIsGone() should set the gone player's camera to NULL in case
the player who left was player 0. This is because if a remaining player
receives a "recoverable" error, they will become player 0. Once that happens,
they game will try to update sounds through their camera and crash in
FMODSoundRenderer::UpdateListener() because the zones array is now NULL.
G_NewInit() should also clear all the player structures.
SVN r233 (trunk)
2006-06-30 02:13:26 +00:00
|
|
|
void C_InitTicker (const char *label, unsigned int max, bool showpercent)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added some hackery at the start of MouseRead_Win32() that prevents it from
yanking the mouse around if they keys haven't been read yet to combat the
same situation that causes the keyboard to return DIERR_NOTACQUIRED in
KeyRead(): The window is sort of in focus and sort of not. User.dll
considers it to be focused and it's drawn as such, but another focused
window is on top of it, and DirectInput doesn't see it as focused.
- Fixed: KeyRead() should handle DIERR_NOTACQUIRED errors the same way it
handles DIERR_INPUTLOST errors. This can happen if our window had the
focus stolen away from it before we tried to acquire the keyboard in
DI_Init2(). Strangely, MouseRead_DI() already did this.
- When a stack overflow occurs, report.txt now only includes the first and
last 16KB of the stack to make it more manageable.
- Limited StreamEditBinary() to the first 64KB of the file to keep it from
taking too long on large dumps.
- And now I know why gathering crash information in the same process that
crashed can be bad: Stack overflows. You get one spare page to play with
when the stack overflows. MiniDumpWriteDump() needs more than that and
causes an access violation when it runs out of leftover stack, silently
terminating the application. Windows XP x64 offers SetThreadStackGuarantee()
to increase this, but that isn't available on anything older, including
32-bit XP. To get around this, a new thread is created to write the mini
dump when the stack overflows.
- Changed A_Burnination() to be closer to Strife's.
- Fixed: When playing back demos, DoAddBot() can be called without an
associated call to SpawnBot(). So if the bot can't spawn, botnum can
go negative, which will cause problems later in DCajunMaster::Main()
when it sees that wanted_botnum (0) is higher than botnum (-1).
- Fixed: Stopping demo recording in multiplayer games should not abruptly
drop the recorder out of the game without notifying the other players.
In fact, there's no reason why it should drop them out of multiplayer at
all.
- Fixed: Earthquakes were unreliable in multiplayer games because
P_PredictPlayer() did not preserve the player's xviewshift.
- Fixed: PlayerIsGone() needs to stop any scripts that belong to the player
who left, in addition to executing disconnect scripts.
- Fixed: APlayerPawn::AddInventory() should also check for a NULL player->mo
in case the player left but somebody still has a reference to their actor.
- Fixed: DDrawFB::PaintToWindow() should simulate proper unlocking behavior
and set Buffer to NULL.
- Improved feedback for network game initialization with the console ticker.
- Moved i_net.cpp and i_net.h out of sdl/ and win32/ and into the main source
directory. They are identical, so keeping two copies of them is bad.
- Fixed: (At least with Creative's driver's,) EAX settings are global and not
per-application. So if you play a multiplayer ZDoom game on one computer
(or even another EAX-using application), ZDoom needs to restore the
environment when it regains focus.
- Maybe fixed: (See http://forum.zdoom.org/potato.php?t=10689) Apparently,
PacketGet can receive ECONNRESET from nodes that aren't in the game. It
should be safe to just ignore these packets.
- Fixed: PlayerIsGone() should set the gone player's camera to NULL in case
the player who left was player 0. This is because if a remaining player
receives a "recoverable" error, they will become player 0. Once that happens,
they game will try to update sounds through their camera and crash in
FMODSoundRenderer::UpdateListener() because the zones array is now NULL.
G_NewInit() should also clear all the player structures.
SVN r233 (trunk)
2006-06-30 02:13:26 +00:00
|
|
|
TickerPercent = showpercent;
|
2006-02-24 04:48:15 +00:00
|
|
|
TickerMax = max;
|
|
|
|
TickerLabel = label;
|
|
|
|
TickerAt = 0;
|
|
|
|
maybedrawnow (true, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_SetTicker (unsigned int at, bool forceUpdate)
|
|
|
|
{
|
|
|
|
TickerAt = at > TickerMax ? TickerMax : at;
|
|
|
|
maybedrawnow (true, TickerVisible ? forceUpdate : false);
|
|
|
|
}
|
|
|
|
|
2007-12-27 04:30:12 +00:00
|
|
|
void C_DrawConsole (bool hw2d)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
static int oldbottom = 0;
|
|
|
|
int lines, left, offset;
|
|
|
|
|
|
|
|
left = LEFTMARGIN;
|
|
|
|
lines = (ConBottom-ConFont->GetHeight()*2)/ConFont->GetHeight();
|
|
|
|
if (-ConFont->GetHeight() + lines*ConFont->GetHeight() > ConBottom - ConFont->GetHeight()*7/2)
|
|
|
|
{
|
|
|
|
offset = -ConFont->GetHeight()/2;
|
|
|
|
lines--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
offset = -ConFont->GetHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ConBottom < oldbottom) &&
|
|
|
|
(gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL) &&
|
|
|
|
(viewwindowx || viewwindowy) &&
|
|
|
|
viewactive)
|
|
|
|
{
|
|
|
|
BorderNeedRefresh = screen->GetPageCount ();
|
|
|
|
}
|
|
|
|
|
|
|
|
oldbottom = ConBottom;
|
|
|
|
|
|
|
|
if (ConsoleState == c_up)
|
|
|
|
{
|
|
|
|
C_DrawNotifyText ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (ConBottom)
|
|
|
|
{
|
|
|
|
int visheight, realheight;
|
|
|
|
FTexture *conpic = TexMan[conback];
|
|
|
|
|
|
|
|
visheight = ConBottom;
|
|
|
|
realheight = (visheight * conpic->GetHeight()) / SCREENHEIGHT;
|
|
|
|
|
|
|
|
screen->DrawTexture (conpic, 0, visheight - screen->GetHeight(),
|
|
|
|
DTA_DestWidth, screen->GetWidth(),
|
|
|
|
DTA_DestHeight, screen->GetHeight(),
|
- Discovered that Shader Model 1.4 clamps my constants, so I can't use
palettes smaller than 256 entries with the shader I wrote for it. Is there
a list of gotchas like this listed some where? I'd really like to see it.
Well, when compiled with SM2.0, the PalTex shader seems to be every-so-
slightly faster on my GF7950GT than the SM1.4 version, so I guess it's a
minor win for cards that support it.
- Fixed: ST_Endoom() failed to free the bitmap it used.
- Added the DTA_ColorOverlay attribute to blend a color with the texture
being drawn. For software, this (currently) only works with black. For
hardware, it works with any color. The motiviation for this was so I could
rewrite the status bar calls that passed DIM_MAP to DTA_Translation to
draw darker icons into something that didn't require making a whole new
remap table.
- After having an "OMG! How could I have been so stupid?" moment, I have
removed the off-by-one check from D3DFB. I had thought the off-by-one error
was caused by rounding errors by the shader hardware. Not so. Rather, I
wasn't sampling what I thought I was sampling. A texture that uses palette
index 255 passes the value 1.0 to the shader. The shader needs to adjust the
range of its palette indexes, or it will end up trying to read color 256
from the palette texture when it should be reading color 255. Doh!
- The TranslationToTable() function has been added to map from translation
numbers used by actors to the tables those numbers represent. This function
performs validation for the input and returns NULL if the input value
is invalid.
- Major changes to the way translation tables work: No longer are they each a
256-byte array. Instead, the FRemapTable structure is used to represent each
one. It includes a remap array for the software renderer, a palette array
for a hardware renderer, and a native texture pointer for D3DFB. The
translationtables array itself is now an array of TArrays that point to the
real tables. The DTA_Translation attribute must also be passed a pointer
to a FRemapTable, not a byte array as previously.
- Modified DFrameBuffer::DrawRateStuff() so that it can do its thing properly
for D3DFB's 2D mode. Before, any fullscreen graphics (like help images)
covered it up.
SVN r640 (trunk)
2007-12-26 04:42:15 +00:00
|
|
|
DTA_ColorOverlay, conshade,
|
2008-02-14 22:58:56 +00:00
|
|
|
DTA_Alpha, (hw2d && gamestate != GS_FULLCONSOLE) ? FLOAT2FIXED(con_alpha) : FRACUNIT,
|
2006-02-24 04:48:15 +00:00
|
|
|
DTA_Masked, false,
|
|
|
|
TAG_DONE);
|
|
|
|
if (conline && visheight < screen->GetHeight())
|
|
|
|
{
|
2007-12-22 04:52:51 +00:00
|
|
|
screen->Clear (0, visheight, screen->GetWidth(), visheight+1, 0, 0);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ConBottom >= 12)
|
|
|
|
{
|
|
|
|
screen->SetFont (ConFont);
|
|
|
|
screen->DrawText (CR_ORANGE, SCREENWIDTH - 8 -
|
|
|
|
ConFont->StringWidth ("v" DOTVERSIONSTR),
|
|
|
|
ConBottom - ConFont->GetHeight() - 4,
|
|
|
|
"v" DOTVERSIONSTR, TAG_DONE);
|
|
|
|
if (TickerMax)
|
|
|
|
{
|
|
|
|
char tickstr[256];
|
|
|
|
const int tickerY = ConBottom - ConFont->GetHeight() - 4;
|
|
|
|
size_t i;
|
|
|
|
int tickend = ConCols - SCREENWIDTH / 90 - 6;
|
|
|
|
int tickbegin = 0;
|
|
|
|
|
|
|
|
if (TickerLabel)
|
|
|
|
{
|
|
|
|
tickbegin = (int)strlen (TickerLabel) + 2;
|
|
|
|
sprintf (tickstr, "%s: ", TickerLabel);
|
|
|
|
}
|
|
|
|
if (tickend > 256 - ConFont->GetCharWidth(0x12))
|
|
|
|
tickend = 256 - ConFont->GetCharWidth(0x12);
|
|
|
|
tickstr[tickbegin] = 0x10;
|
|
|
|
memset (tickstr + tickbegin + 1, 0x11, tickend - tickbegin);
|
|
|
|
tickstr[tickend + 1] = 0x12;
|
|
|
|
tickstr[tickend + 2] = ' ';
|
- Added some hackery at the start of MouseRead_Win32() that prevents it from
yanking the mouse around if they keys haven't been read yet to combat the
same situation that causes the keyboard to return DIERR_NOTACQUIRED in
KeyRead(): The window is sort of in focus and sort of not. User.dll
considers it to be focused and it's drawn as such, but another focused
window is on top of it, and DirectInput doesn't see it as focused.
- Fixed: KeyRead() should handle DIERR_NOTACQUIRED errors the same way it
handles DIERR_INPUTLOST errors. This can happen if our window had the
focus stolen away from it before we tried to acquire the keyboard in
DI_Init2(). Strangely, MouseRead_DI() already did this.
- When a stack overflow occurs, report.txt now only includes the first and
last 16KB of the stack to make it more manageable.
- Limited StreamEditBinary() to the first 64KB of the file to keep it from
taking too long on large dumps.
- And now I know why gathering crash information in the same process that
crashed can be bad: Stack overflows. You get one spare page to play with
when the stack overflows. MiniDumpWriteDump() needs more than that and
causes an access violation when it runs out of leftover stack, silently
terminating the application. Windows XP x64 offers SetThreadStackGuarantee()
to increase this, but that isn't available on anything older, including
32-bit XP. To get around this, a new thread is created to write the mini
dump when the stack overflows.
- Changed A_Burnination() to be closer to Strife's.
- Fixed: When playing back demos, DoAddBot() can be called without an
associated call to SpawnBot(). So if the bot can't spawn, botnum can
go negative, which will cause problems later in DCajunMaster::Main()
when it sees that wanted_botnum (0) is higher than botnum (-1).
- Fixed: Stopping demo recording in multiplayer games should not abruptly
drop the recorder out of the game without notifying the other players.
In fact, there's no reason why it should drop them out of multiplayer at
all.
- Fixed: Earthquakes were unreliable in multiplayer games because
P_PredictPlayer() did not preserve the player's xviewshift.
- Fixed: PlayerIsGone() needs to stop any scripts that belong to the player
who left, in addition to executing disconnect scripts.
- Fixed: APlayerPawn::AddInventory() should also check for a NULL player->mo
in case the player left but somebody still has a reference to their actor.
- Fixed: DDrawFB::PaintToWindow() should simulate proper unlocking behavior
and set Buffer to NULL.
- Improved feedback for network game initialization with the console ticker.
- Moved i_net.cpp and i_net.h out of sdl/ and win32/ and into the main source
directory. They are identical, so keeping two copies of them is bad.
- Fixed: (At least with Creative's driver's,) EAX settings are global and not
per-application. So if you play a multiplayer ZDoom game on one computer
(or even another EAX-using application), ZDoom needs to restore the
environment when it regains focus.
- Maybe fixed: (See http://forum.zdoom.org/potato.php?t=10689) Apparently,
PacketGet can receive ECONNRESET from nodes that aren't in the game. It
should be safe to just ignore these packets.
- Fixed: PlayerIsGone() should set the gone player's camera to NULL in case
the player who left was player 0. This is because if a remaining player
receives a "recoverable" error, they will become player 0. Once that happens,
they game will try to update sounds through their camera and crash in
FMODSoundRenderer::UpdateListener() because the zones array is now NULL.
G_NewInit() should also clear all the player structures.
SVN r233 (trunk)
2006-06-30 02:13:26 +00:00
|
|
|
if (TickerPercent)
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
sprintf (tickstr + tickend + 3, "%d%%", Scale (TickerAt, 100, TickerMax));
|
- Added some hackery at the start of MouseRead_Win32() that prevents it from
yanking the mouse around if they keys haven't been read yet to combat the
same situation that causes the keyboard to return DIERR_NOTACQUIRED in
KeyRead(): The window is sort of in focus and sort of not. User.dll
considers it to be focused and it's drawn as such, but another focused
window is on top of it, and DirectInput doesn't see it as focused.
- Fixed: KeyRead() should handle DIERR_NOTACQUIRED errors the same way it
handles DIERR_INPUTLOST errors. This can happen if our window had the
focus stolen away from it before we tried to acquire the keyboard in
DI_Init2(). Strangely, MouseRead_DI() already did this.
- When a stack overflow occurs, report.txt now only includes the first and
last 16KB of the stack to make it more manageable.
- Limited StreamEditBinary() to the first 64KB of the file to keep it from
taking too long on large dumps.
- And now I know why gathering crash information in the same process that
crashed can be bad: Stack overflows. You get one spare page to play with
when the stack overflows. MiniDumpWriteDump() needs more than that and
causes an access violation when it runs out of leftover stack, silently
terminating the application. Windows XP x64 offers SetThreadStackGuarantee()
to increase this, but that isn't available on anything older, including
32-bit XP. To get around this, a new thread is created to write the mini
dump when the stack overflows.
- Changed A_Burnination() to be closer to Strife's.
- Fixed: When playing back demos, DoAddBot() can be called without an
associated call to SpawnBot(). So if the bot can't spawn, botnum can
go negative, which will cause problems later in DCajunMaster::Main()
when it sees that wanted_botnum (0) is higher than botnum (-1).
- Fixed: Stopping demo recording in multiplayer games should not abruptly
drop the recorder out of the game without notifying the other players.
In fact, there's no reason why it should drop them out of multiplayer at
all.
- Fixed: Earthquakes were unreliable in multiplayer games because
P_PredictPlayer() did not preserve the player's xviewshift.
- Fixed: PlayerIsGone() needs to stop any scripts that belong to the player
who left, in addition to executing disconnect scripts.
- Fixed: APlayerPawn::AddInventory() should also check for a NULL player->mo
in case the player left but somebody still has a reference to their actor.
- Fixed: DDrawFB::PaintToWindow() should simulate proper unlocking behavior
and set Buffer to NULL.
- Improved feedback for network game initialization with the console ticker.
- Moved i_net.cpp and i_net.h out of sdl/ and win32/ and into the main source
directory. They are identical, so keeping two copies of them is bad.
- Fixed: (At least with Creative's driver's,) EAX settings are global and not
per-application. So if you play a multiplayer ZDoom game on one computer
(or even another EAX-using application), ZDoom needs to restore the
environment when it regains focus.
- Maybe fixed: (See http://forum.zdoom.org/potato.php?t=10689) Apparently,
PacketGet can receive ECONNRESET from nodes that aren't in the game. It
should be safe to just ignore these packets.
- Fixed: PlayerIsGone() should set the gone player's camera to NULL in case
the player who left was player 0. This is because if a remaining player
receives a "recoverable" error, they will become player 0. Once that happens,
they game will try to update sounds through their camera and crash in
FMODSoundRenderer::UpdateListener() because the zones array is now NULL.
G_NewInit() should also clear all the player structures.
SVN r233 (trunk)
2006-06-30 02:13:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tickstr[tickend+3] = 0;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
screen->DrawText (CR_BROWN, LEFTMARGIN, tickerY, tickstr, TAG_DONE);
|
|
|
|
|
|
|
|
// Draw the marker
|
|
|
|
i = LEFTMARGIN+5+tickbegin*8 + Scale (TickerAt, (SDWORD)(tickend - tickbegin)*8, TickerMax);
|
|
|
|
screen->DrawChar (CR_ORANGE, (int)i, tickerY, 0x13, TAG_DONE);
|
|
|
|
|
|
|
|
TickerVisible = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TickerVisible = false;
|
|
|
|
}
|
|
|
|
}
|
2006-07-09 20:04:05 +00:00
|
|
|
|
|
|
|
// Apply palette blend effects
|
|
|
|
if (StatusBar != NULL)
|
|
|
|
{
|
|
|
|
player_t *player = StatusBar->CPlayer;
|
|
|
|
if (player->camera != NULL && player->camera->player != NULL)
|
|
|
|
{
|
|
|
|
player = player->camera->player;
|
|
|
|
}
|
2007-01-23 01:43:47 +00:00
|
|
|
if (player->BlendA != 0 && (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL))
|
2007-01-22 23:55:46 +00:00
|
|
|
{
|
|
|
|
screen->Dim (PalEntry ((unsigned char)(player->BlendR*255), (unsigned char)(player->BlendG*255), (unsigned char)(player->BlendB*255)),
|
|
|
|
player->BlendA, 0, ConBottom, screen->GetWidth(), screen->GetHeight() - ConBottom);
|
|
|
|
SB_state = screen->GetPageCount ();
|
|
|
|
BorderNeedRefresh = screen->GetPageCount ();
|
|
|
|
}
|
2006-07-09 20:04:05 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (menuactive != MENU_Off)
|
|
|
|
{
|
|
|
|
screen->SetFont (SmallFont);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lines > 0)
|
|
|
|
{
|
|
|
|
int bottomline = ConBottom - ConFont->GetHeight()*2 - 4;
|
|
|
|
int pos = (InsertLine - 1) & LINEMASK;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
screen->SetFont (ConFont);
|
|
|
|
|
|
|
|
ConsoleDrawing = true;
|
|
|
|
|
|
|
|
for (i = RowAdjust; i; i--)
|
|
|
|
{
|
|
|
|
if (pos == TopLine)
|
|
|
|
{
|
|
|
|
RowAdjust = RowAdjust - i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pos = (pos - 1) & LINEMASK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
pos = (pos - 1) & LINEMASK;
|
|
|
|
if (Lines[pos] != NULL)
|
|
|
|
{
|
|
|
|
screen->DrawText (CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(),
|
|
|
|
Lines[pos], TAG_DONE);
|
|
|
|
}
|
|
|
|
lines--;
|
|
|
|
} while (pos != TopLine && lines > 0);
|
|
|
|
|
|
|
|
ConsoleDrawing = false;
|
|
|
|
DequeueConsoleText ();
|
|
|
|
|
|
|
|
if (ConBottom >= 20)
|
|
|
|
{
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
if (gamestate != GS_STARTUP)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
// Make a copy of the command line, in case an input event is handled
|
|
|
|
// while we draw the console and it changes.
|
|
|
|
CmdLine[2+CmdLine[0]] = 0;
|
2006-05-03 22:45:01 +00:00
|
|
|
FString command((char *)&CmdLine[2+CmdLine[259]]);
|
2006-02-24 04:48:15 +00:00
|
|
|
int cursorpos = CmdLine[1] - CmdLine[259];
|
|
|
|
|
|
|
|
screen->DrawChar (CR_ORANGE, left, bottomline, '\x1c', TAG_DONE);
|
|
|
|
screen->DrawText (CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline,
|
2006-05-03 22:45:01 +00:00
|
|
|
command, TAG_DONE);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (cursoron)
|
|
|
|
{
|
|
|
|
screen->DrawChar (CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb),
|
|
|
|
bottomline, '\xb', TAG_DONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (RowAdjust && ConBottom >= ConFont->GetHeight()*7/2)
|
|
|
|
{
|
|
|
|
// Indicate that the view has been scrolled up (10)
|
|
|
|
// and if we can scroll no further (12)
|
|
|
|
screen->DrawChar (CR_GREEN, 0, bottomline, pos == TopLine ? 12 : 10, TAG_DONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
screen->SetFont (SmallFont);
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_FullConsole ()
|
|
|
|
{
|
|
|
|
if (demoplayback)
|
|
|
|
G_CheckDemoStatus ();
|
- Added some hackery at the start of MouseRead_Win32() that prevents it from
yanking the mouse around if they keys haven't been read yet to combat the
same situation that causes the keyboard to return DIERR_NOTACQUIRED in
KeyRead(): The window is sort of in focus and sort of not. User.dll
considers it to be focused and it's drawn as such, but another focused
window is on top of it, and DirectInput doesn't see it as focused.
- Fixed: KeyRead() should handle DIERR_NOTACQUIRED errors the same way it
handles DIERR_INPUTLOST errors. This can happen if our window had the
focus stolen away from it before we tried to acquire the keyboard in
DI_Init2(). Strangely, MouseRead_DI() already did this.
- When a stack overflow occurs, report.txt now only includes the first and
last 16KB of the stack to make it more manageable.
- Limited StreamEditBinary() to the first 64KB of the file to keep it from
taking too long on large dumps.
- And now I know why gathering crash information in the same process that
crashed can be bad: Stack overflows. You get one spare page to play with
when the stack overflows. MiniDumpWriteDump() needs more than that and
causes an access violation when it runs out of leftover stack, silently
terminating the application. Windows XP x64 offers SetThreadStackGuarantee()
to increase this, but that isn't available on anything older, including
32-bit XP. To get around this, a new thread is created to write the mini
dump when the stack overflows.
- Changed A_Burnination() to be closer to Strife's.
- Fixed: When playing back demos, DoAddBot() can be called without an
associated call to SpawnBot(). So if the bot can't spawn, botnum can
go negative, which will cause problems later in DCajunMaster::Main()
when it sees that wanted_botnum (0) is higher than botnum (-1).
- Fixed: Stopping demo recording in multiplayer games should not abruptly
drop the recorder out of the game without notifying the other players.
In fact, there's no reason why it should drop them out of multiplayer at
all.
- Fixed: Earthquakes were unreliable in multiplayer games because
P_PredictPlayer() did not preserve the player's xviewshift.
- Fixed: PlayerIsGone() needs to stop any scripts that belong to the player
who left, in addition to executing disconnect scripts.
- Fixed: APlayerPawn::AddInventory() should also check for a NULL player->mo
in case the player left but somebody still has a reference to their actor.
- Fixed: DDrawFB::PaintToWindow() should simulate proper unlocking behavior
and set Buffer to NULL.
- Improved feedback for network game initialization with the console ticker.
- Moved i_net.cpp and i_net.h out of sdl/ and win32/ and into the main source
directory. They are identical, so keeping two copies of them is bad.
- Fixed: (At least with Creative's driver's,) EAX settings are global and not
per-application. So if you play a multiplayer ZDoom game on one computer
(or even another EAX-using application), ZDoom needs to restore the
environment when it regains focus.
- Maybe fixed: (See http://forum.zdoom.org/potato.php?t=10689) Apparently,
PacketGet can receive ECONNRESET from nodes that aren't in the game. It
should be safe to just ignore these packets.
- Fixed: PlayerIsGone() should set the gone player's camera to NULL in case
the player who left was player 0. This is because if a remaining player
receives a "recoverable" error, they will become player 0. Once that happens,
they game will try to update sounds through their camera and crash in
FMODSoundRenderer::UpdateListener() because the zones array is now NULL.
G_NewInit() should also clear all the player structures.
SVN r233 (trunk)
2006-06-30 02:13:26 +00:00
|
|
|
D_QuitNetGame ();
|
2006-02-24 04:48:15 +00:00
|
|
|
advancedemo = false;
|
|
|
|
ConsoleState = c_down;
|
|
|
|
HistPos = NULL;
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
if (gamestate != GS_STARTUP)
|
|
|
|
{
|
|
|
|
gamestate = GS_FULLCONSOLE;
|
|
|
|
level.music = NULL;
|
|
|
|
S_Start ();
|
2006-05-11 04:00:58 +00:00
|
|
|
P_FreeLevelData ();
|
2006-02-24 04:48:15 +00:00
|
|
|
V_SetBlend (0,0,0,0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
C_AdjustBottom ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_ToggleConsole ()
|
|
|
|
{
|
|
|
|
if (gamestate == GS_DEMOSCREEN || demoplayback)
|
|
|
|
{
|
|
|
|
gameaction = ga_fullconsole;
|
|
|
|
}
|
|
|
|
else if (!chatmodeon && (ConsoleState == c_up || ConsoleState == c_rising) && menuactive == MENU_Off)
|
|
|
|
{
|
|
|
|
ConsoleState = c_falling;
|
|
|
|
HistPos = NULL;
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
}
|
|
|
|
else if (gamestate != GS_FULLCONSOLE && gamestate != GS_STARTUP)
|
|
|
|
{
|
|
|
|
ConsoleState = c_rising;
|
|
|
|
C_FlushDisplay ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_HideConsole ()
|
|
|
|
{
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
if (gamestate != GS_FULLCONSOLE)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
ConsoleState = c_up;
|
|
|
|
ConBottom = 0;
|
|
|
|
HistPos = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void makestartposgood ()
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
int pos = CmdLine[259];
|
|
|
|
int curs = CmdLine[1];
|
|
|
|
int len = CmdLine[0];
|
|
|
|
|
|
|
|
n = pos;
|
|
|
|
|
|
|
|
if (pos >= len)
|
|
|
|
{ // Start of visible line is beyond end of line
|
|
|
|
n = curs - ConCols + 2;
|
|
|
|
}
|
|
|
|
if ((curs - pos) >= ConCols - 2)
|
|
|
|
{ // The cursor is beyond the visible part of the line
|
|
|
|
n = curs - ConCols + 2;
|
|
|
|
}
|
|
|
|
if (pos > curs)
|
|
|
|
{ // The cursor is in front of the visible part of the line
|
|
|
|
n = curs;
|
|
|
|
}
|
|
|
|
if (n < 0)
|
|
|
|
n = 0;
|
|
|
|
CmdLine[259] = n;
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int data1 = ev->data1;
|
|
|
|
|
|
|
|
switch (ev->subtype)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
|
|
|
|
case EV_GUI_Char:
|
|
|
|
// Add keypress to command line
|
|
|
|
if (buffer[0] < len)
|
|
|
|
{
|
|
|
|
if (buffer[1] == buffer[0])
|
|
|
|
{
|
|
|
|
buffer[buffer[0] + 2] = ev->data1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *c, *e;
|
|
|
|
|
|
|
|
e = (char *)&buffer[buffer[0] + 1];
|
|
|
|
c = (char *)&buffer[buffer[1] + 2];
|
|
|
|
|
|
|
|
for (; e >= c; e--)
|
|
|
|
*(e + 1) = *e;
|
|
|
|
|
|
|
|
*c = ev->data1;
|
|
|
|
}
|
|
|
|
buffer[0]++;
|
|
|
|
buffer[1]++;
|
|
|
|
makestartposgood ();
|
|
|
|
HistPos = NULL;
|
|
|
|
}
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EV_GUI_WheelUp:
|
|
|
|
case EV_GUI_WheelDown:
|
|
|
|
if (!(ev->data3 & GKM_SHIFT))
|
|
|
|
{
|
|
|
|
data1 = GK_PGDN + ev->subtype - EV_GUI_WheelDown;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
data1 = GK_DOWN + ev->subtype - EV_GUI_WheelDown;
|
|
|
|
}
|
|
|
|
// Intentional fallthrough
|
|
|
|
|
|
|
|
case EV_GUI_KeyDown:
|
|
|
|
case EV_GUI_KeyRepeat:
|
|
|
|
switch (data1)
|
|
|
|
{
|
|
|
|
case '\t':
|
|
|
|
// Try to do tab-completion
|
|
|
|
C_TabComplete ((ev->data3 & GKM_SHIFT) ? false : true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_PGUP:
|
|
|
|
if (ev->data3 & (GKM_SHIFT|GKM_CTRL))
|
|
|
|
{ // Scroll console buffer up one page
|
|
|
|
RowAdjust += (SCREENHEIGHT-4) /
|
|
|
|
((gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP) ? ConFont->GetHeight() : ConFont->GetHeight()*2) - 3;
|
|
|
|
}
|
|
|
|
else if (RowAdjust < CONSOLELINES)
|
|
|
|
{ // Scroll console buffer up
|
|
|
|
if (ev->subtype == EV_GUI_WheelUp)
|
|
|
|
{
|
|
|
|
RowAdjust += 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RowAdjust++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_PGDN:
|
|
|
|
if (ev->data3 & (GKM_SHIFT|GKM_CTRL))
|
|
|
|
{ // Scroll console buffer down one page
|
|
|
|
const int scrollamt = (SCREENHEIGHT-4) /
|
|
|
|
((gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP) ? ConFont->GetHeight() : ConFont->GetHeight()*2) - 3;
|
|
|
|
if (RowAdjust < scrollamt)
|
|
|
|
{
|
|
|
|
RowAdjust = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RowAdjust -= scrollamt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (RowAdjust > 0)
|
|
|
|
{ // Scroll console buffer down
|
|
|
|
if (ev->subtype == EV_GUI_WheelDown)
|
|
|
|
{
|
|
|
|
RowAdjust = MAX (0, RowAdjust - 3);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RowAdjust--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_HOME:
|
|
|
|
if (ev->data3 & GKM_CTRL)
|
|
|
|
{ // Move to top of console buffer
|
|
|
|
RowAdjust = CONSOLELINES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Move cursor to start of line
|
|
|
|
buffer[1] = buffer[len+4] = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_END:
|
|
|
|
if (ev->data3 & GKM_CTRL)
|
|
|
|
{ // Move to bottom of console buffer
|
|
|
|
RowAdjust = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Move cursor to end of line
|
|
|
|
buffer[1] = buffer[0];
|
|
|
|
makestartposgood ();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_LEFT:
|
|
|
|
// Move cursor left one character
|
|
|
|
if (buffer[1])
|
|
|
|
{
|
|
|
|
buffer[1]--;
|
|
|
|
makestartposgood ();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_RIGHT:
|
|
|
|
// Move cursor right one character
|
|
|
|
if (buffer[1] < buffer[0])
|
|
|
|
{
|
|
|
|
buffer[1]++;
|
|
|
|
makestartposgood ();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\b':
|
|
|
|
// Erase character to left of cursor
|
|
|
|
if (buffer[0] && buffer[1])
|
|
|
|
{
|
|
|
|
char *c, *e;
|
|
|
|
|
|
|
|
e = (char *)&buffer[buffer[0] + 2];
|
|
|
|
c = (char *)&buffer[buffer[1] + 2];
|
|
|
|
|
|
|
|
for (; c < e; c++)
|
|
|
|
*(c - 1) = *c;
|
|
|
|
|
|
|
|
buffer[0]--;
|
|
|
|
buffer[1]--;
|
|
|
|
if (buffer[len+4])
|
|
|
|
buffer[len+4]--;
|
|
|
|
makestartposgood ();
|
|
|
|
}
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_DEL:
|
|
|
|
// Erase character under cursor
|
|
|
|
if (buffer[1] < buffer[0])
|
|
|
|
{
|
|
|
|
char *c, *e;
|
|
|
|
|
|
|
|
e = (char *)&buffer[buffer[0] + 2];
|
|
|
|
c = (char *)&buffer[buffer[1] + 3];
|
|
|
|
|
|
|
|
for (; c < e; c++)
|
|
|
|
*(c - 1) = *c;
|
|
|
|
|
|
|
|
buffer[0]--;
|
|
|
|
makestartposgood ();
|
|
|
|
}
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_UP:
|
|
|
|
// Move to previous entry in the command history
|
|
|
|
if (HistPos == NULL)
|
|
|
|
{
|
|
|
|
HistPos = HistHead;
|
|
|
|
}
|
|
|
|
else if (HistPos->Older)
|
|
|
|
{
|
|
|
|
HistPos = HistPos->Older;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HistPos)
|
|
|
|
{
|
|
|
|
strcpy ((char *)&buffer[2], HistPos->String);
|
|
|
|
buffer[0] = buffer[1] = (BYTE)strlen ((char *)&buffer[2]);
|
|
|
|
buffer[len+4] = 0;
|
|
|
|
makestartposgood();
|
|
|
|
}
|
|
|
|
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GK_DOWN:
|
|
|
|
// Move to next entry in the command history
|
|
|
|
if (HistPos && HistPos->Newer)
|
|
|
|
{
|
|
|
|
HistPos = HistPos->Newer;
|
|
|
|
|
|
|
|
strcpy ((char *)&buffer[2], HistPos->String);
|
|
|
|
buffer[0] = buffer[1] = (BYTE)strlen ((char *)&buffer[2]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HistPos = NULL;
|
|
|
|
buffer[0] = buffer[1] = 0;
|
|
|
|
}
|
|
|
|
buffer[len+4] = 0;
|
|
|
|
makestartposgood();
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'X':
|
|
|
|
if (ev->data3 & GKM_CTRL)
|
|
|
|
{
|
|
|
|
buffer[1] = buffer[0] = 0;
|
|
|
|
TabbedLast = TabbedList = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'D':
|
|
|
|
if (ev->data3 & GKM_CTRL && buffer[0] == 0)
|
|
|
|
{ // Control-D pressed on an empty line
|
|
|
|
int replen = (int)strlen (con_ctrl_d);
|
|
|
|
|
|
|
|
if (replen == 0)
|
|
|
|
break; // Replacement is empty, so do nothing
|
|
|
|
|
|
|
|
if (replen > len)
|
|
|
|
replen = len;
|
|
|
|
|
|
|
|
memcpy (&buffer[2], con_ctrl_d, replen);
|
|
|
|
buffer[0] = buffer[1] = replen;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Intentional fall-through for command(s) added with Ctrl-D
|
|
|
|
|
|
|
|
case '\r':
|
|
|
|
// Execute command line (ENTER)
|
|
|
|
|
|
|
|
buffer[2 + buffer[0]] = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < buffer[0] && isspace(buffer[2+i]); ++i)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
if (i == buffer[0])
|
|
|
|
{
|
|
|
|
// Command line is empty, so do nothing to the history
|
|
|
|
}
|
|
|
|
else if (HistHead && stricmp (HistHead->String, (char *)&buffer[2]) == 0)
|
|
|
|
{
|
|
|
|
// Command line was the same as the previous one,
|
|
|
|
// so leave the history list alone
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Command line is different from last command line,
|
|
|
|
// or there is nothing in the history list,
|
|
|
|
// so add it to the history list.
|
|
|
|
|
2006-05-04 03:49:46 +00:00
|
|
|
History *temp = (History *)M_Malloc (sizeof(struct History) + buffer[0]);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
strcpy (temp->String, (char *)&buffer[2]);
|
|
|
|
temp->Older = HistHead;
|
|
|
|
if (HistHead)
|
|
|
|
{
|
|
|
|
HistHead->Newer = temp;
|
|
|
|
}
|
|
|
|
temp->Newer = NULL;
|
|
|
|
HistHead = temp;
|
|
|
|
|
|
|
|
if (!HistTail)
|
|
|
|
{
|
|
|
|
HistTail = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HistSize == MAXHISTSIZE)
|
|
|
|
{
|
|
|
|
HistTail = HistTail->Newer;
|
2008-02-17 02:40:03 +00:00
|
|
|
M_Free (HistTail->Older);
|
2006-02-24 04:48:15 +00:00
|
|
|
HistTail->Older = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HistSize++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
HistPos = NULL;
|
|
|
|
Printf (127, TEXTCOLOR_WHITE "]%s\n", &buffer[2]);
|
|
|
|
buffer[0] = buffer[1] = buffer[len+4] = 0;
|
|
|
|
AddCommandString ((char *)&buffer[2]);
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '`':
|
|
|
|
case GK_ESCAPE:
|
|
|
|
// Close console and clear command line. But if we're in the
|
|
|
|
// fullscreen console mode, there's nothing to fall back on
|
|
|
|
// if it's closed, so open the main menu instead.
|
|
|
|
if (gamestate == GS_STARTUP)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (gamestate == GS_FULLCONSOLE)
|
|
|
|
{
|
|
|
|
C_DoCommand ("menu_main");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buffer[0] = buffer[1] = buffer[len+4] = 0;
|
|
|
|
HistPos = NULL;
|
|
|
|
C_ToggleConsole ();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'C':
|
|
|
|
case 'V':
|
|
|
|
TabbedLast = false;
|
|
|
|
TabbedList = false;
|
|
|
|
if (ev->data3 & GKM_CTRL)
|
|
|
|
{
|
|
|
|
if (data1 == 'C')
|
|
|
|
{ // copy to clipboard
|
|
|
|
if (buffer[0] > 0)
|
|
|
|
{
|
|
|
|
buffer[2 + buffer[0]] = 0;
|
|
|
|
I_PutInClipboard ((char *)&buffer[2]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // paste from clipboard
|
|
|
|
char *clip = I_GetFromClipboard ();
|
|
|
|
if (clip != NULL)
|
|
|
|
{
|
|
|
|
strtok (clip, "\r\n\b");
|
|
|
|
int cliplen = (int)strlen (clip);
|
|
|
|
|
|
|
|
cliplen = MIN(len, cliplen);
|
|
|
|
if (buffer[0] + cliplen > len)
|
|
|
|
{
|
|
|
|
cliplen = len - buffer[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cliplen > 0)
|
|
|
|
{
|
|
|
|
if (buffer[1] < buffer[0])
|
|
|
|
{
|
|
|
|
memmove (&buffer[2 + buffer[1] + cliplen],
|
|
|
|
&buffer[2 + buffer[1]], buffer[0] - buffer[1]);
|
|
|
|
}
|
|
|
|
memcpy (&buffer[2 + buffer[1]], clip, cliplen);
|
|
|
|
buffer[0] += cliplen;
|
|
|
|
buffer[1] += cliplen;
|
|
|
|
makestartposgood ();
|
|
|
|
HistPos = NULL;
|
|
|
|
}
|
|
|
|
delete[] clip;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Ensure that the cursor is always visible while typing
|
|
|
|
CursorTicker = C_BLINKRATE;
|
|
|
|
cursoron = 1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
bool C_Responder (event_t *ev)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (ev->type != EV_GUI_Event ||
|
|
|
|
ConsoleState == c_up ||
|
|
|
|
ConsoleState == c_rising ||
|
|
|
|
menuactive != MENU_Off)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return C_HandleKey (ev, CmdLine, 255);
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (history)
|
|
|
|
{
|
|
|
|
struct History *hist = HistTail;
|
|
|
|
|
|
|
|
while (hist)
|
|
|
|
{
|
|
|
|
Printf (" %s\n", hist->String);
|
|
|
|
hist = hist->Newer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (clear)
|
|
|
|
{
|
|
|
|
C_FlushDisplay ();
|
|
|
|
ClearConsole ();
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (echo)
|
|
|
|
{
|
|
|
|
int last = argv.argc()-1;
|
|
|
|
for (int i = 1; i <= last; ++i)
|
|
|
|
{
|
2007-01-09 04:40:58 +00:00
|
|
|
strbin (argv[i]);
|
2006-02-24 04:48:15 +00:00
|
|
|
Printf ("%s%s", argv[i], i!=last ? " " : "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Printing in the middle of the screen */
|
|
|
|
|
|
|
|
CVAR (Float, con_midtime, 3.f, CVAR_ARCHIVE)
|
|
|
|
|
|
|
|
static const char bar1[] = TEXTCOLOR_RED "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
|
|
|
|
"\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_TAN "\n";
|
|
|
|
static const char bar2[] = TEXTCOLOR_RED "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
|
|
|
|
"\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_GREEN "\n";
|
|
|
|
static const char bar3[] = TEXTCOLOR_RED "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
|
|
|
|
"\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_NORMAL "\n";
|
|
|
|
static const char logbar[] = "\n<------------------------------->\n";
|
|
|
|
|
|
|
|
void C_MidPrint (const char *msg)
|
|
|
|
{
|
2006-07-19 16:00:44 +00:00
|
|
|
if (StatusBar == NULL)
|
|
|
|
return;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (msg)
|
|
|
|
{
|
|
|
|
AddToConsole (-1, bar1);
|
|
|
|
AddToConsole (-1, msg);
|
|
|
|
AddToConsole (-1, bar3);
|
|
|
|
if (Logfile)
|
|
|
|
{
|
|
|
|
fputs (logbar, Logfile);
|
|
|
|
fputs (msg, Logfile);
|
|
|
|
fputs (logbar, Logfile);
|
|
|
|
fflush (Logfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
StatusBar->AttachMessage (new DHUDMessage (msg, 1.5f, 0.375f, 0, 0,
|
|
|
|
(EColorRange)PrintColors[PRINTLEVELS], con_midtime), MAKE_ID('C','N','T','R'));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StatusBar->DetachMessage (MAKE_ID('C','N','T','R'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_MidPrintBold (const char *msg)
|
|
|
|
{
|
|
|
|
if (msg)
|
|
|
|
{
|
|
|
|
AddToConsole (-1, bar2);
|
|
|
|
AddToConsole (-1, msg);
|
|
|
|
AddToConsole (-1, bar3);
|
|
|
|
if (Logfile)
|
|
|
|
{
|
|
|
|
fputs (logbar, Logfile);
|
|
|
|
fputs (msg, Logfile);
|
|
|
|
fputs (logbar, Logfile);
|
|
|
|
fflush (Logfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
StatusBar->AttachMessage (new DHUDMessage (msg, 1.5f, 0.375f, 0, 0,
|
|
|
|
(EColorRange)PrintColors[PRINTLEVELS+1], con_midtime), MAKE_ID('C','N','T','R'));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StatusBar->DetachMessage (MAKE_ID('C','N','T','R'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****** Tab completion code ******/
|
|
|
|
|
2006-05-04 03:49:46 +00:00
|
|
|
struct TabData
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int UseCount;
|
2006-05-11 04:00:58 +00:00
|
|
|
FName TabName;
|
2006-05-04 03:49:46 +00:00
|
|
|
|
|
|
|
TabData()
|
|
|
|
: UseCount(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TabData(const char *name)
|
2006-05-11 04:00:58 +00:00
|
|
|
: UseCount(1), TabName(name)
|
2006-05-04 03:49:46 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TabData(const TabData &other)
|
2006-05-11 04:00:58 +00:00
|
|
|
: UseCount(other.UseCount), TabName(other.TabName)
|
2006-05-04 03:49:46 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2006-05-06 23:43:44 +00:00
|
|
|
static TArray<TabData> TabCommands (TArray<TabData>::NoInit);
|
2006-02-24 04:48:15 +00:00
|
|
|
static int TabPos; // Last TabCommand tabbed to
|
|
|
|
static int TabStart; // First char in CmdLine to use for tab completion
|
|
|
|
static int TabSize; // Size of tab string
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
static bool FindTabCommand (const char *name, int *stoppos, int len)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
FName aname(name);
|
|
|
|
unsigned int i;
|
|
|
|
int cval = 1;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-05-04 03:49:46 +00:00
|
|
|
for (i = 0; i < TabCommands.Size(); i++)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
if (TabCommands[i].TabName == aname)
|
|
|
|
{
|
|
|
|
*stoppos = i;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
cval = strnicmp (TabCommands[i].TabName.GetChars(), name, len);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (cval >= 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*stoppos = i;
|
|
|
|
|
|
|
|
return (cval == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_AddTabCommand (const char *name)
|
|
|
|
{
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
if (FindTabCommand (name, &pos, INT_MAX))
|
|
|
|
{
|
|
|
|
TabCommands[pos].UseCount++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-05-04 03:49:46 +00:00
|
|
|
TabData tab(name);
|
|
|
|
TabCommands.Insert (pos, tab);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_RemoveTabCommand (const char *name)
|
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
FName aname(name, true);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-05-11 04:00:58 +00:00
|
|
|
if (aname == NAME_None)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (unsigned int i = 0; i < TabCommands.Size(); ++i)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
if (TabCommands[i].TabName == aname)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
if (--TabCommands[i].UseCount == 0)
|
|
|
|
{
|
|
|
|
TabCommands.Delete(i);
|
|
|
|
}
|
|
|
|
break;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-12 03:14:40 +00:00
|
|
|
void C_ClearTabCommands ()
|
|
|
|
{
|
|
|
|
TabCommands.Clear();
|
|
|
|
}
|
|
|
|
|
2006-05-11 04:00:58 +00:00
|
|
|
static int FindDiffPoint (FName name1, const char *str2)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
const char *str1 = name1.GetChars();
|
2006-02-24 04:48:15 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; tolower(str1[i]) == tolower(str2[i]); i++)
|
|
|
|
if (str1[i] == 0 || str2[i] == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void C_TabComplete (bool goForward)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int diffpoint;
|
|
|
|
|
|
|
|
if (!TabbedLast)
|
|
|
|
{
|
|
|
|
bool cancomplete;
|
|
|
|
|
|
|
|
// Skip any spaces at beginning of command line
|
|
|
|
if (CmdLine[2] == ' ')
|
|
|
|
{
|
|
|
|
for (i = 0; i < CmdLine[0]; i++)
|
|
|
|
if (CmdLine[2+i] != ' ')
|
|
|
|
break;
|
|
|
|
|
|
|
|
TabStart = i + 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TabStart = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TabStart == CmdLine[0] + 2)
|
|
|
|
return; // Line was nothing but spaces
|
|
|
|
|
|
|
|
TabSize = CmdLine[0] - TabStart + 2;
|
|
|
|
|
|
|
|
if (!FindTabCommand ((char *)(CmdLine + TabStart), &TabPos, TabSize))
|
|
|
|
return; // No initial matches
|
|
|
|
|
|
|
|
// Show a list of possible completions, if more than one.
|
|
|
|
if (TabbedList || con_notablist)
|
|
|
|
{
|
|
|
|
cancomplete = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cancomplete = C_TabCompleteList ();
|
|
|
|
TabbedList = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (goForward)
|
|
|
|
{ // Position just before the list of completions so that when TabPos
|
|
|
|
// gets advanced below, it will be at the first one.
|
|
|
|
--TabPos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Find the last matching tab, then go one past it.
|
2006-05-16 04:19:20 +00:00
|
|
|
while (++TabPos < (int)TabCommands.Size())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
if (FindDiffPoint (TabCommands[TabPos].TabName, (char *)(CmdLine + TabStart)) < TabSize)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TabbedLast = true;
|
|
|
|
if (!cancomplete)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-16 04:19:20 +00:00
|
|
|
if ((goForward && ++TabPos == (int)TabCommands.Size()) ||
|
2006-02-24 04:48:15 +00:00
|
|
|
(!goForward && --TabPos < 0))
|
|
|
|
{
|
|
|
|
TabbedLast = false;
|
|
|
|
CmdLine[0] = CmdLine[1] = TabSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
diffpoint = FindDiffPoint (TabCommands[TabPos].TabName, (char *)(CmdLine + TabStart));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (diffpoint < TabSize)
|
|
|
|
{
|
|
|
|
// No more matches
|
|
|
|
TabbedLast = false;
|
|
|
|
CmdLine[0] = CmdLine[1] = TabSize + TabStart - 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
strcpy ((char *)(CmdLine + TabStart), TabCommands[TabPos].TabName.GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
CmdLine[0] = CmdLine[1] = (BYTE)strlen ((char *)(CmdLine + 2)) + 1;
|
|
|
|
CmdLine[CmdLine[0] + 1] = ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
makestartposgood ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool C_TabCompleteList ()
|
|
|
|
{
|
|
|
|
int nummatches, i;
|
|
|
|
size_t maxwidth;
|
|
|
|
int commonsize = INT_MAX;
|
|
|
|
|
|
|
|
nummatches = 0;
|
|
|
|
maxwidth = 0;
|
|
|
|
|
2006-05-16 04:19:20 +00:00
|
|
|
for (i = TabPos; i < (int)TabCommands.Size(); ++i)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
if (FindDiffPoint (TabCommands[i].TabName, (char *)(CmdLine + TabStart)) < TabSize)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (i > TabPos)
|
|
|
|
{
|
|
|
|
// This keeps track of the longest common prefix for all the possible
|
|
|
|
// completions, so we can fill in part of the command for the user if
|
|
|
|
// the longest common prefix is longer than what the user already typed.
|
2006-05-11 04:00:58 +00:00
|
|
|
int diffpt = FindDiffPoint (TabCommands[i-1].TabName, TabCommands[i].TabName.GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
if (diffpt < commonsize)
|
|
|
|
{
|
|
|
|
commonsize = diffpt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nummatches++;
|
2006-05-11 04:00:58 +00:00
|
|
|
maxwidth = MAX (maxwidth, strlen (TabCommands[i].TabName.GetChars()));
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (nummatches > 1)
|
|
|
|
{
|
|
|
|
size_t x = 0;
|
|
|
|
maxwidth += 3;
|
|
|
|
Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLine+2);
|
|
|
|
for (i = TabPos; nummatches > 0; ++i, --nummatches)
|
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
Printf ("%-*s", int(maxwidth), TabCommands[i].TabName.GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
x += maxwidth;
|
|
|
|
if (x > ConCols - maxwidth)
|
|
|
|
{
|
|
|
|
x = 0;
|
|
|
|
Printf ("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (x != 0)
|
|
|
|
{
|
|
|
|
Printf ("\n");
|
|
|
|
}
|
|
|
|
// Fill in the longest common prefix, if it's longer than what was typed.
|
|
|
|
if (TabSize != commonsize)
|
|
|
|
{
|
|
|
|
TabSize = commonsize;
|
2006-05-11 04:00:58 +00:00
|
|
|
strncpy ((char *)CmdLine + TabStart, TabCommands[TabPos].TabName.GetChars(), commonsize);
|
2006-02-24 04:48:15 +00:00
|
|
|
CmdLine[0] = TabStart + commonsize - 2;
|
|
|
|
CmdLine[1] = CmdLine[0];
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|