diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 64665ff88..7b194f6f7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,7 @@ if( WIN32 ) else() if( APPLE ) set( NO_GTK ON ) + set( DYN_GTK OFF ) # Prevent inclusion of fp.h and FixMath.h from Carbon framework # Declarations from these files are not used but cause the following conflicts: @@ -128,25 +129,41 @@ else() add_definitions( -D__FP__ -D__FIXMATH__ ) else() option( NO_GTK "Disable GTK+ dialogs (Not applicable to Windows)" ) + option( DYN_GTK "Load GTK+ at runtime instead of compile time" ON ) option( VALGRIND "Add special Valgrind sequences to self-modifying code" ) # Use GTK+ for the IWAD picker, if available. if( NOT NO_GTK ) - pkg_check_modules( GTK2 gtk+-2.0 ) - if( GTK2_FOUND ) - set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} ) - include_directories( ${GTK2_INCLUDE_DIRS} ) - link_directories( ${GTK2_LIBRARY_DIRS} ) + pkg_check_modules( GTK3 gtk+-3.0 ) + if( GTK3_FOUND ) + if( NOT DYN_GTK ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK3_LIBRARIES} ) + endif() + include_directories( ${GTK3_INCLUDE_DIRS} ) + link_directories( ${GTK3_LIBRARY_DIRS} ) else() - set( NO_GTK ON ) + pkg_check_modules( GTK2 gtk+-2.0 ) + if( GTK2_FOUND ) + if( NOT DYN_GTK ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} ) + endif() + include_directories( ${GTK2_INCLUDE_DIRS} ) + link_directories( ${GTK2_LIBRARY_DIRS} ) + else() + set( NO_GTK ON ) + endif() endif() endif() endif() if( NO_GTK ) - add_definitions( -DNO_GTK=1 ) + add_definitions( -DNO_GTK ) + elseif( DYN_GTK ) + add_definitions( -DDYN_GTK=1 ) + else() + add_definitions( -DDYN_GTK=0 ) endif() - + # Non-Windows version also needs SDL except native OS X backend if( NOT APPLE OR NOT OSX_COCOA_BACKEND ) find_package( SDL2 REQUIRED ) @@ -455,7 +472,8 @@ set( PLAT_SDL_SOURCES posix/sdl/sdlglvideo.cpp posix/sdl/st_start.cpp ) set( PLAT_UNIX_SOURCES - posix/unix/i_specialpaths.cpp ) + posix/unix/i_specialpaths.cpp + posix/unix/iwadpicker_gtk.cpp ) set( PLAT_OSX_SOURCES posix/osx/iwadpicker_cocoa.mm posix/osx/i_specialpaths.mm @@ -914,6 +932,7 @@ set (PCH_SOURCES gi.cpp gitinfo.cpp hu_scores.cpp + i_module.cpp i_net.cpp info.cpp keysections.cpp diff --git a/src/am_map.cpp b/src/am_map.cpp index 333dd4146..d172d7a93 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2047,7 +2047,8 @@ void AM_drawSubsectors() scale / scaley, rotation, colormap, - floorlight + floorlight, + f_y + f_h ); } } diff --git a/src/b_think.cpp b/src/b_think.cpp index 5a98b5c1a..d097db864 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -358,7 +358,7 @@ void DBot::WhatToGet (AActor *item) } else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere) return; - else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && player->mo->health >= deh.MaxHealth /*MAXHEALTH*/) + else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina) return; if ((dest == NULL || diff --git a/src/c_console.cpp b/src/c_console.cpp index 2d164c478..3064abb0d 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -100,7 +100,7 @@ extern bool advancedemo; extern FBaseCVar *CVars; extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE]; -int ConCols, PhysRows; +unsigned ConCols; int ConWidth; bool vidactive = false; bool cursoron = false; @@ -112,12 +112,13 @@ constate_e ConsoleState = c_up; static int TopLine, InsertLine; static void ClearConsole (); -static void C_PasteText(FString clip, BYTE *buffer, int len); struct GameAtExit { + GameAtExit(FString str) : Command(str) {} + GameAtExit *Next; - char Command[1]; + FString Command; }; static GameAtExit *ExitCmdList; @@ -139,27 +140,9 @@ static bool ConsoleDrawing; static char *work = NULL; static int worklen = 0; - -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 -static BYTE CmdLine[260]; - -#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, 1, CVAR_ARCHIVE) // Scale notify text at high resolutions? +CVAR(Float, con_notifytime, 3.f, CVAR_ARCHIVE) +CVAR(Bool, con_centernotify, false, CVAR_ARCHIVE) +CUSTOM_CVAR(Int, con_scaletext, 1, CVAR_ARCHIVE) // Scale notify text at high resolutions? { if (self < 0) self = 0; if (self > 3) self = 3; @@ -172,10 +155,16 @@ CUSTOM_CVAR(Int, con_scale, 0, CVAR_ARCHIVE) int active_con_scale() { - if (con_scale == 0) - return uiscale; - else - return con_scale; + int scale = con_scale; + if (scale <= 0) + { + scale = CleanXfac - 1; + if (scale <= 0) + { + scale = 1; + } + } + return scale; } int active_con_scaletext() @@ -197,19 +186,229 @@ CUSTOM_CVAR(Float, con_alpha, 0.75f, CVAR_ARCHIVE) } // Command to run when Ctrl-D is pressed at start of line -CVAR (String, con_ctrl_d, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(String, con_ctrl_d, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + + +struct History +{ + struct History *Older; + struct History *Newer; + FString String; +}; + +struct FCommandBuffer +{ + FString Text; // The actual command line text + unsigned CursorPos; + unsigned StartPos; // First character to display + + FCommandBuffer() + { + CursorPos = StartPos = 0; + } + + FCommandBuffer(const FCommandBuffer &o) + { + Text = o.Text; + CursorPos = o.CursorPos; + StartPos = o.StartPos; + } + + void Draw(int x, int y, int scale, bool cursor) + { + if (scale == 1) + { + screen->DrawChar(ConFont, CR_ORANGE, x, y, '\x1c', TAG_DONE); + screen->DrawText(ConFont, CR_ORANGE, x + ConFont->GetCharWidth(0x1c), y, + &Text[StartPos], TAG_DONE); + + if (cursor) + { + screen->DrawChar(ConFont, CR_YELLOW, + x + ConFont->GetCharWidth(0x1c) + (CursorPos - StartPos) * ConFont->GetCharWidth(0xb), + y, '\xb', TAG_DONE); + } + } + else + { + screen->DrawChar(ConFont, CR_ORANGE, x, y, '\x1c', + DTA_VirtualWidth, screen->GetWidth() / scale, + DTA_VirtualHeight, screen->GetHeight() / scale, + DTA_KeepRatio, true, TAG_DONE); + + screen->DrawText(ConFont, CR_ORANGE, x + ConFont->GetCharWidth(0x1c), y, + &Text[StartPos], + DTA_VirtualWidth, screen->GetWidth() / scale, + DTA_VirtualHeight, screen->GetHeight() / scale, + DTA_KeepRatio, true, TAG_DONE); + + if (cursor) + { + screen->DrawChar(ConFont, CR_YELLOW, + x + ConFont->GetCharWidth(0x1c) + (CursorPos - StartPos) * ConFont->GetCharWidth(0xb), + y, '\xb', + DTA_VirtualWidth, screen->GetWidth() / scale, + DTA_VirtualHeight, screen->GetHeight() / scale, + DTA_KeepRatio, true, TAG_DONE); + } + } + } + + void MakeStartPosGood() + { + int n = StartPos; + unsigned cols = ConCols / active_con_scale(); + + if (StartPos >= Text.Len()) + { // Start of visible line is beyond end of line + n = CursorPos - cols + 2; + } + if ((CursorPos - StartPos) >= cols - 2) + { // The cursor is beyond the visible part of the line + n = CursorPos - cols + 2; + } + if (StartPos > CursorPos) + { // The cursor is in front of the visible part of the line + n = CursorPos; + } + StartPos = MAX(0, n); + } + + void CursorStart() + { + CursorPos = 0; + StartPos = 0; + } + + void CursorEnd() + { + CursorPos = (unsigned)Text.Len(); + StartPos = 0; + MakeStartPosGood(); + } + + void CursorLeft() + { + if (CursorPos > 0) + { + CursorPos--; + MakeStartPosGood(); + } + } + + void CursorRight() + { + if (CursorPos < Text.Len()) + { + CursorPos++; + MakeStartPosGood(); + } + } + + void DeleteLeft() + { + if (CursorPos > 0) + { + Text.Remove(CursorPos - 1, 1); + CursorPos--; + MakeStartPosGood(); + } + } + + void DeleteRight() + { + if (CursorPos < Text.Len()) + { + Text.Remove(CursorPos, 1); + MakeStartPosGood(); + } + } + + void AddChar(int character) + { + ///FIXME: Not Unicode-aware + if (CursorPos == Text.Len()) + { + Text += char(character); + } + else + { + char foo = char(character); + Text.Insert(CursorPos, &foo, 1); + } + CursorPos++; + MakeStartPosGood(); + } + + void AddString(FString clip) + { + if (clip.IsNotEmpty()) + { + // Only paste the first line. + long brk = clip.IndexOfAny("\r\n\b"); + if (brk >= 0) + { + clip.Truncate(brk); + } + if (Text.IsEmpty()) + { + Text = clip; + } + else + { + Text.Insert(CursorPos, clip); + } + CursorPos += (unsigned)clip.Len(); + MakeStartPosGood(); + } + } + + void SetString(FString str) + { + Text = str; + CursorPos = (unsigned)Text.Len(); + MakeStartPosGood(); + } +}; +static FCommandBuffer CmdLine; + +#define MAXHISTSIZE 50 +static struct History *HistHead = NULL, *HistTail = NULL, *HistPos = NULL; +static int HistSize; #define NUMNOTIFIES 4 #define NOTIFYFADETIME 6 -static struct NotifyText +struct FNotifyText { int TimeOut; int PrintLevel; FString Text; -} NotifyStrings[NUMNOTIFIES]; +}; + +struct FNotifyBuffer +{ +public: + FNotifyBuffer(); + void AddString(int printlevel, FString source); + void Shift(int maxlines); + void Clear() { Text.Clear(); } + void Tick(); + void Draw(); + +private: + TArray Text; + int Top; + int TopGoal; + enum { NEWLINE, APPENDLINE, REPLACELINE } AddType; +}; +static FNotifyBuffer NotifyStrings; + +CUSTOM_CVAR(Int, con_notifylines, NUMNOTIFIES, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) +{ + NotifyStrings.Shift(self); +} -static int NotifyTop, NotifyTopGoal; int PrintColors[PRINTLEVELS+2] = { CR_RED, CR_GOLD, CR_GRAY, CR_GREEN, CR_GREEN, CR_GOLD }; @@ -217,8 +416,6 @@ static void setmsgcolor (int index, int color); FILE *Logfile = NULL; -void C_AddNotifyString (int printlevel, const char *source); - FIntCVar msglevel ("msg", 0, CVAR_ARCHIVE); @@ -312,7 +509,7 @@ void DequeueConsoleText () TextQueue *next = queued->Next; if (queued->bNotify) { - C_AddNotifyString (queued->PrintLevel, queued->Text); + NotifyStrings.AddString(queued->PrintLevel, queued->Text); } else { @@ -358,7 +555,6 @@ void C_InitConsole (int width, int height, bool ingame) } ConWidth = (width - LEFTMARGIN - RIGHTMARGIN); ConCols = ConWidth / cwidth; - PhysRows = height / cheight; if (conbuffer == NULL) conbuffer = new FConsoleBuffer; } @@ -377,16 +573,14 @@ CCMD (atexit) GameAtExit *record = ExitCmdList; while (record != NULL) { - Printf ("%s\n", record->Command); + Printf ("%s\n", record->Command.GetChars()); 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]); + GameAtExit *record = new GameAtExit(argv[i]); record->Next = ExitCmdList; ExitCmdList = record; } @@ -408,8 +602,8 @@ void C_DeinitConsole () while (cmd != NULL) { GameAtExit *next = cmd->Next; - AddCommandString (cmd->Command); - M_Free (cmd); + AddCommandString (cmd->Command.LockBuffer()); + delete cmd; cmd = next; } @@ -419,7 +613,7 @@ void C_DeinitConsole () while (hist != NULL) { History *next = hist->Newer; - free (hist); + delete hist; hist = next; } HistTail = HistHead = HistPos = NULL; @@ -494,22 +688,30 @@ static void setmsgcolor (int index, int color) extern int DisplayWidth; -void C_AddNotifyString (int printlevel, const char *source) +FNotifyBuffer::FNotifyBuffer() { - static enum - { - NEWLINE, - APPENDLINE, - REPLACELINE - } addtype = NEWLINE; + Top = TopGoal = 0; + AddType = NEWLINE; +} +void FNotifyBuffer::Shift(int maxlines) +{ + if (maxlines >= 0 && Text.Size() > (unsigned)maxlines) + { + Text.Delete(0, Text.Size() - maxlines); + } +} + +void FNotifyBuffer::AddString(int printlevel, FString source) +{ FBrokenLines *lines; - int i, len, width; + int i, width; if ((printlevel != 128 && !show_messages) || - !(len = (int)strlen (source)) || + source.IsEmpty() || gamestate == GS_FULLCONSOLE || - gamestate == GS_DEMOSCREEN) + gamestate == GS_DEMOSCREEN || + con_notifylines == 0) return; if (ConsoleDrawing) @@ -527,15 +729,18 @@ void C_AddNotifyString (int printlevel, const char *source) width = DisplayWidth / active_con_scaletext(); } - if (addtype == APPENDLINE && NotifyStrings[NUMNOTIFIES-1].PrintLevel == printlevel) + if (AddType == APPENDLINE && Text.Size() > 0 && Text[Text.Size() - 1].PrintLevel == printlevel) { - FString str = NotifyStrings[NUMNOTIFIES-1].Text + source; + FString str = Text[Text.Size() - 1].Text + source; lines = V_BreakLines (SmallFont, width, str); } else { lines = V_BreakLines (SmallFont, width, source); - addtype = (addtype == APPENDLINE) ? NEWLINE : addtype; + if (AddType == APPENDLINE) + { + AddType = NEWLINE; + } } if (lines == NULL) @@ -543,30 +748,37 @@ void C_AddNotifyString (int printlevel, const char *source) for (i = 0; lines[i].Width >= 0; i++) { - if (addtype == NEWLINE) + FNotifyText newline; + + newline.Text = lines[i].Text; + newline.TimeOut = gametic + int(con_notifytime * TICRATE); + newline.PrintLevel = printlevel; + if (AddType == NEWLINE || Text.Size() == 0) { - for (int j = 0; j < NUMNOTIFIES-1; ++j) + if (con_notifylines > 0) { - NotifyStrings[j] = NotifyStrings[j+1]; + Shift(con_notifylines - 1); } + Text.Push(newline); } - NotifyStrings[NUMNOTIFIES-1].Text = lines[i].Text; - NotifyStrings[NUMNOTIFIES-1].TimeOut = gametic + (int)(con_notifytime * TICRATE); - NotifyStrings[NUMNOTIFIES-1].PrintLevel = printlevel; - addtype = NEWLINE; + else + { + Text[Text.Size() - 1] = newline; + } + AddType = NEWLINE; } V_FreeBrokenLines (lines); lines = NULL; - switch (source[len-1]) + switch (source[source.Len()-1]) { - case '\r': addtype = REPLACELINE; break; - case '\n': addtype = NEWLINE; break; - default: addtype = APPENDLINE; break; + case '\r': AddType = REPLACELINE; break; + case '\n': AddType = NEWLINE; break; + default: AddType = APPENDLINE; break; } - NotifyTopGoal = 0; + TopGoal = 0; } void AddToConsole (int printlevel, const char *text) @@ -589,7 +801,7 @@ int PrintString (int printlevel, const char *outline) AddToConsole (printlevel, outline); if (vidactive && screen && SmallFont) { - C_AddNotifyString (printlevel, outline); + NotifyStrings.AddString(printlevel, outline); maybedrawnow (false, false); } } @@ -657,10 +869,7 @@ int DPrintf (int level, const char *format, ...) void C_FlushDisplay () { - int i; - - for (i = 0; i < NUMNOTIFIES; i++) - NotifyStrings[i].TimeOut = 0; + NotifyStrings.Clear(); } void C_AdjustBottom () @@ -679,7 +888,7 @@ void C_NewModeAdjust () } int consoletic = 0; -void C_Ticker () +void C_Ticker() { static int lasttic = 0; consoletic++; @@ -721,28 +930,43 @@ void C_Ticker () } lasttic = consoletic; + NotifyStrings.Tick(); +} - if (NotifyTopGoal > NotifyTop) +void FNotifyBuffer::Tick() +{ + if (TopGoal > Top) { - NotifyTop++; + Top++; } - else if (NotifyTopGoal < NotifyTop) + else if (TopGoal < Top) { - NotifyTop--; + Top--; + } + + // Remove lines from the beginning that have expired. + unsigned i; + for (i = 0; i < Text.Size(); ++i) + { + if (Text[i].TimeOut != 0 && Text[i].TimeOut > gametic) + break; + } + if (i > 0) + { + Text.Delete(0, i); } } -static void C_DrawNotifyText () +void FNotifyBuffer::Draw() { bool center = (con_centernotify != 0.f); - int i, line, lineadv, color, j, skip; + int line, lineadv, color, j; bool canskip; if (gamestate == GS_FULLCONSOLE || gamestate == GS_DEMOSCREEN/* || menuactive != MENU_Off*/) return; - line = NotifyTop; - skip = 0; + line = Top; canskip = true; lineadv = SmallFont->GetHeight (); @@ -753,67 +977,60 @@ static void C_DrawNotifyText () BorderTopRefresh = screen->GetPageCount (); - for (i = 0; i < NUMNOTIFIES; i++) + for (unsigned i = 0; i < Text.Size(); ++ i) { - if (NotifyStrings[i].TimeOut == 0) + FNotifyText ¬ify = Text[i]; + + if (notify.TimeOut == 0) continue; - j = NotifyStrings[i].TimeOut - gametic; + j = notify.TimeOut - gametic; if (j > 0) { - if (!show_messages && NotifyStrings[i].PrintLevel != 128) + if (!show_messages && notify.PrintLevel != 128) continue; - double alpha; + double alpha = (j < NOTIFYFADETIME) ? 1. * j / NOTIFYFADETIME : 1; - if (j < NOTIFYFADETIME) - { - alpha = 1. * j / NOTIFYFADETIME; - } - else - { - alpha = 1; - } - - if (NotifyStrings[i].PrintLevel >= PRINTLEVELS) + if (notify.PrintLevel >= PRINTLEVELS) color = CR_UNTRANSLATED; else - color = PrintColors[NotifyStrings[i].PrintLevel]; + color = PrintColors[notify.PrintLevel]; if (active_con_scaletext() == 0) { if (!center) - screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, + screen->DrawText (SmallFont, color, 0, line, notify.Text, DTA_CleanNoMove, true, DTA_AlphaF, alpha, TAG_DONE); else screen->DrawText (SmallFont, color, (SCREENWIDTH - - SmallFont->StringWidth (NotifyStrings[i].Text)*CleanXfac)/2, - line, NotifyStrings[i].Text, DTA_CleanNoMove, true, + SmallFont->StringWidth (notify.Text)*CleanXfac)/2, + line, notify.Text, DTA_CleanNoMove, true, DTA_AlphaF, alpha, TAG_DONE); } else if (active_con_scaletext() == 1) { if (!center) - screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, + screen->DrawText (SmallFont, color, 0, line, notify.Text, DTA_AlphaF, alpha, TAG_DONE); else screen->DrawText (SmallFont, color, (SCREENWIDTH - - SmallFont->StringWidth (NotifyStrings[i].Text))/2, - line, NotifyStrings[i].Text, + SmallFont->StringWidth (notify.Text))/2, + line, notify.Text, DTA_AlphaF, alpha, TAG_DONE); } else { if (!center) - screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, + screen->DrawText (SmallFont, color, 0, line, notify.Text, DTA_VirtualWidth, screen->GetWidth() / active_con_scaletext(), DTA_VirtualHeight, screen->GetHeight() / active_con_scaletext(), DTA_KeepRatio, true, DTA_AlphaF, alpha, TAG_DONE); else screen->DrawText (SmallFont, color, (screen->GetWidth() - - SmallFont->StringWidth (NotifyStrings[i].Text) * active_con_scaletext()) / 2 / active_con_scaletext(), - line, NotifyStrings[i].Text, + SmallFont->StringWidth (notify.Text) * active_con_scaletext()) / 2 / active_con_scaletext(), + line, notify.Text, DTA_VirtualWidth, screen->GetWidth() / active_con_scaletext(), DTA_VirtualHeight, screen->GetHeight() / active_con_scaletext(), DTA_KeepRatio, true, @@ -826,16 +1043,15 @@ static void C_DrawNotifyText () { if (canskip) { - NotifyTop += lineadv; + Top += lineadv; line += lineadv; - skip++; } - NotifyStrings[i].TimeOut = 0; + notify.TimeOut = 0; } } if (canskip) { - NotifyTop = NotifyTopGoal; + Top = TopGoal; } } @@ -860,8 +1076,6 @@ void C_DrawConsole (bool hw2d) int lines, left, offset; int textScale = active_con_scale(); - if (textScale == 0) - textScale = CleanXfac; left = LEFTMARGIN; lines = (ConBottom/textScale-ConFont->GetHeight()*2)/ConFont->GetHeight(); @@ -887,7 +1101,7 @@ void C_DrawConsole (bool hw2d) if (ConsoleState == c_up) { - C_DrawNotifyText (); + NotifyStrings.Draw(); return; } else if (ConBottom) @@ -1037,44 +1251,8 @@ void C_DrawConsole (bool hw2d) { // 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; - FString command((char *)&CmdLine[2+CmdLine[259]]); - int cursorpos = CmdLine[1] - CmdLine[259]; - - if (textScale == 1) - { - screen->DrawChar(ConFont, CR_ORANGE, left, bottomline, '\x1c', TAG_DONE); - screen->DrawText(ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline, - command, TAG_DONE); - - if (cursoron) - { - screen->DrawChar(ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb), - bottomline, '\xb', TAG_DONE); - } - } - else - { - screen->DrawChar(ConFont, CR_ORANGE, left, bottomline, '\x1c', - DTA_VirtualWidth, screen->GetWidth() / textScale, - DTA_VirtualHeight, screen->GetHeight() / textScale, - DTA_KeepRatio, true, TAG_DONE); - - screen->DrawText(ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline, - command, - DTA_VirtualWidth, screen->GetWidth() / textScale, - DTA_VirtualHeight, screen->GetHeight() / textScale, - DTA_KeepRatio, true, TAG_DONE); - - if (cursoron) - { - screen->DrawChar(ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb), - bottomline, '\xb', - DTA_VirtualWidth, screen->GetWidth() / textScale, - DTA_VirtualHeight, screen->GetHeight() / textScale, - DTA_KeepRatio, true, TAG_DONE); - } - } + FCommandBuffer command(CmdLine); + command.Draw(left, bottomline, textScale, cursoron); } if (RowAdjust && ConBottom >= ConFont->GetHeight()*7/2) { @@ -1146,35 +1324,8 @@ void C_HideConsole () } } -static void makestartposgood () +static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer) { - 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; -} - -static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) -{ - int i; int data1 = ev->data1; switch (ev->subtype) @@ -1184,29 +1335,8 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case EV_GUI_Char: // Add keypress to command line - if (buffer[0] < len) - { - if (buffer[1] == buffer[0]) - { - buffer[buffer[0] + 2] = BYTE(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 = char(ev->data1); - } - buffer[0]++; - buffer[1]++; - makestartposgood (); - HistPos = NULL; - } + buffer.AddChar(ev->data1); + HistPos = NULL; TabbedLast = false; TabbedList = false; break; @@ -1235,7 +1365,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case GK_PGUP: if (ev->data3 & (GKM_SHIFT|GKM_CTRL)) { // Scroll console buffer up one page - RowAdjust += (SCREENHEIGHT-4) / + RowAdjust += (SCREENHEIGHT-4)/active_con_scale() / ((gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP) ? ConFont->GetHeight() : ConFont->GetHeight()*2) - 3; } else if (RowAdjust < conbuffer->GetFormattedLineCount()) @@ -1258,7 +1388,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case GK_PGDN: if (ev->data3 & (GKM_SHIFT|GKM_CTRL)) { // Scroll console buffer down one page - const int scrollamt = (SCREENHEIGHT-4) / + const int scrollamt = (SCREENHEIGHT-4)/active_con_scale() / ((gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP) ? ConFont->GetHeight() : ConFont->GetHeight()*2) - 3; if (RowAdjust < scrollamt) { @@ -1289,7 +1419,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) } else { // Move cursor to start of line - buffer[1] = buffer[len+4] = 0; + buffer.CursorStart(); } break; @@ -1300,66 +1430,30 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) } else { // Move cursor to end of line - buffer[1] = buffer[0]; - makestartposgood (); + buffer.CursorEnd(); } break; case GK_LEFT: // Move cursor left one character - if (buffer[1]) - { - buffer[1]--; - makestartposgood (); - } + buffer.CursorLeft(); break; case GK_RIGHT: // Move cursor right one character - if (buffer[1] < buffer[0]) - { - buffer[1]++; - makestartposgood (); - } + buffer.CursorRight(); 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 (); - } + buffer.DeleteLeft(); 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 (); - } + buffer.DeleteRight(); TabbedLast = false; TabbedList = false; break; @@ -1377,10 +1471,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) if (HistPos) { - strcpy ((char *)&buffer[2], HistPos->String); - buffer[0] = buffer[1] = (BYTE)strlen ((char *)&buffer[2]); - buffer[len+4] = 0; - makestartposgood(); + buffer.SetString(HistPos->String); } TabbedLast = false; @@ -1392,17 +1483,13 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) if (HistPos && HistPos->Newer) { HistPos = HistPos->Newer; - - strcpy ((char *)&buffer[2], HistPos->String); - buffer[0] = buffer[1] = (BYTE)strlen ((char *)&buffer[2]); + buffer.SetString(HistPos->String); } else { HistPos = NULL; - buffer[0] = buffer[1] = 0; + buffer.SetString(""); } - buffer[len+4] = 0; - makestartposgood(); TabbedLast = false; TabbedList = false; break; @@ -1410,24 +1497,19 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case 'X': if (ev->data3 & GKM_CTRL) { - buffer[1] = buffer[0] = 0; + buffer.SetString(""); TabbedLast = TabbedList = false; } break; case 'D': - if (ev->data3 & GKM_CTRL && buffer[0] == 0) + if (ev->data3 & GKM_CTRL && buffer.Text.Len() == 0) { // Control-D pressed on an empty line - int replen = (int)strlen (con_ctrl_d); - - if (replen == 0) + if (strlen(con_ctrl_d) == 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; + } + buffer.SetString(*con_ctrl_d); } else { @@ -1438,16 +1520,16 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case '\r': // Execute command line (ENTER) - buffer[2 + buffer[0]] = 0; + buffer.Text.StripLeftRight(); + Printf(127, TEXTCOLOR_WHITE "]%s\n", buffer.Text.GetChars()); + AddCommandString(buffer.Text.LockBuffer()); + buffer.Text.UnlockBuffer(); - for (i = 0; i < buffer[0] && isspace(buffer[2+i]); ++i) - { - } - if (i == buffer[0]) + if (buffer.Text.Len() == 0) { // Command line is empty, so do nothing to the history } - else if (HistHead && stricmp (HistHead->String, (char *)&buffer[2]) == 0) + else if (HistHead && HistHead->String.CompareNoCase(buffer.Text) == 0) { // Command line was the same as the previous one, // so leave the history list alone @@ -1458,9 +1540,8 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) // or there is nothing in the history list, // so add it to the history list. - History *temp = (History *)M_Malloc (sizeof(struct History) + buffer[0]); - - strcpy (temp->String, (char *)&buffer[2]); + History *temp = new History; + temp->String = buffer.Text; temp->Older = HistHead; if (HistHead) { @@ -1477,7 +1558,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) if (HistSize == MAXHISTSIZE) { HistTail = HistTail->Newer; - M_Free (HistTail->Older); + delete HistTail->Older; HistTail->Older = NULL; } else @@ -1486,9 +1567,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) } } HistPos = NULL; - Printf (127, TEXTCOLOR_WHITE "]%s\n", &buffer[2]); - buffer[0] = buffer[1] = buffer[len+4] = 0; - AddCommandString ((char *)&buffer[2]); + buffer.SetString(""); TabbedLast = false; TabbedList = false; break; @@ -1514,7 +1593,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) } else { - buffer[0] = buffer[1] = buffer[len+4] = 0; + buffer.SetString(""); HistPos = NULL; C_ToggleConsole (); } @@ -1532,15 +1611,15 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) { if (data1 == 'C') { // copy to clipboard - if (buffer[0] > 0) + if (buffer.Text.IsNotEmpty()) { - buffer[2 + buffer[0]] = 0; - I_PutInClipboard ((char *)&buffer[2]); + I_PutInClipboard(buffer.Text); } } else { // paste from clipboard - C_PasteText(I_GetFromClipboard(false), buffer, len); + buffer.AddString(I_GetFromClipboard(false)); + HistPos = NULL; } break; } @@ -1550,7 +1629,8 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) #ifdef __unix__ case EV_GUI_MButtonDown: - C_PasteText(I_GetFromClipboard(true), buffer, len); + buffer.AddString(I_GetFromClipboard(true)); + HistPos = NULL; break; #endif } @@ -1560,36 +1640,6 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) return true; } -static void C_PasteText(FString clip, BYTE *buffer, int len) -{ - if (clip.IsNotEmpty()) - { - // Only paste the first line. - long brk = clip.IndexOfAny("\r\n\b"); - int cliplen = brk >= 0 ? brk : (int)clip.Len(); - - // Make sure there's room for the whole thing. - 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; - } - } -} - bool C_Responder (event_t *ev) { if (ev->type != EV_GUI_Event || @@ -1600,7 +1650,7 @@ bool C_Responder (event_t *ev) return false; } - return C_HandleKey (ev, CmdLine, 255); + return C_HandleKey(ev, CmdLine); } CCMD (history) @@ -1609,7 +1659,7 @@ CCMD (history) while (hist) { - Printf (" %s\n", hist->String); + Printf (" %s\n", hist->String.GetChars()); hist = hist->Newer; } } @@ -1793,7 +1843,7 @@ static int FindDiffPoint (FName name1, const char *str2) static void C_TabComplete (bool goForward) { - int i; + unsigned i; int diffpoint; if (!TabbedLast) @@ -1801,25 +1851,20 @@ static void C_TabComplete (bool goForward) bool cancomplete; // Skip any spaces at beginning of command line - if (CmdLine[2] == ' ') + for (i = 0; i < CmdLine.Text.Len(); ++i) { - for (i = 0; i < CmdLine[0]; i++) - if (CmdLine[2+i] != ' ') - break; - - TabStart = i + 2; + if (CmdLine.Text[i] != ' ') + break; } - else - { - TabStart = 2; + if (i == CmdLine.Text.Len()) + { // Line was nothing but spaces + return; } + TabStart = i; - if (TabStart == CmdLine[0] + 2) - return; // Line was nothing but spaces + TabSize = (int)CmdLine.Text.Len() - TabStart; - TabSize = CmdLine[0] - TabStart + 2; - - if (!FindTabCommand ((char *)(CmdLine + TabStart), &TabPos, TabSize)) + if (!FindTabCommand(&CmdLine.Text[TabStart], &TabPos, TabSize)) return; // No initial matches // Show a list of possible completions, if more than one. @@ -1842,7 +1887,7 @@ static void C_TabComplete (bool goForward) { // Find the last matching tab, then go one past it. while (++TabPos < (int)TabCommands.Size()) { - if (FindDiffPoint (TabCommands[TabPos].TabName, (char *)(CmdLine + TabStart)) < TabSize) + if (FindDiffPoint(TabCommands[TabPos].TabName, &CmdLine.Text[TabStart]) < TabSize) { break; } @@ -1859,27 +1904,26 @@ static void C_TabComplete (bool goForward) (!goForward && --TabPos < 0)) { TabbedLast = false; - CmdLine[0] = CmdLine[1] = TabSize; + CmdLine.Text.Truncate(TabSize); } else { - diffpoint = FindDiffPoint (TabCommands[TabPos].TabName, (char *)(CmdLine + TabStart)); + diffpoint = FindDiffPoint(TabCommands[TabPos].TabName, &CmdLine.Text[TabStart]); if (diffpoint < TabSize) { // No more matches TabbedLast = false; - CmdLine[0] = CmdLine[1] = TabSize + TabStart - 2; + CmdLine.Text.Truncate(TabSize - TabStart); } else - { - strcpy ((char *)(CmdLine + TabStart), TabCommands[TabPos].TabName.GetChars()); - CmdLine[0] = CmdLine[1] = (BYTE)strlen ((char *)(CmdLine + 2)) + 1; - CmdLine[CmdLine[0] + 1] = ' '; + { + CmdLine.Text.Truncate(TabStart); + CmdLine.Text << TabCommands[TabPos].TabName << ' '; } } - - makestartposgood (); + CmdLine.CursorPos = (unsigned)CmdLine.Text.Len(); + CmdLine.MakeStartPosGood(); } static bool C_TabCompleteList () @@ -1893,7 +1937,7 @@ static bool C_TabCompleteList () for (i = TabPos; i < (int)TabCommands.Size(); ++i) { - if (FindDiffPoint (TabCommands[i].TabName, (char *)(CmdLine + TabStart)) < TabSize) + if (FindDiffPoint (TabCommands[i].TabName, &CmdLine.Text[TabStart]) < TabSize) { break; } @@ -1918,7 +1962,7 @@ static bool C_TabCompleteList () { size_t x = 0; maxwidth += 3; - Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLine+2); + Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLine.Text.GetChars()); for (i = TabPos; nummatches > 0; ++i, --nummatches) { // [Dusk] Print console commands blue, CVars green, aliases red. @@ -1936,7 +1980,7 @@ static bool C_TabCompleteList () Printf ("%s%-*s", colorcode, int(maxwidth), TabCommands[i].TabName.GetChars()); x += maxwidth; - if (x > ConCols - maxwidth) + if (x > ConCols / active_con_scale() - maxwidth) { x = 0; Printf ("\n"); @@ -1950,9 +1994,9 @@ static bool C_TabCompleteList () if (TabSize != commonsize) { TabSize = commonsize; - strncpy ((char *)CmdLine + TabStart, TabCommands[TabPos].TabName.GetChars(), commonsize); - CmdLine[0] = TabStart + commonsize - 2; - CmdLine[1] = CmdLine[0]; + CmdLine.Text.Truncate(TabStart); + CmdLine.Text.AppendCStrPart(TabCommands[TabPos].TabName.GetChars(), commonsize); + CmdLine.CursorPos = (unsigned)CmdLine.Text.Len(); } return false; } diff --git a/src/g_game.cpp b/src/g_game.cpp index 62d859721..9b518d6ba 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1173,7 +1173,7 @@ void G_Ticker () } // check for turbo cheats - if (cmd->ucmd.forwardmove > TURBOTHRESHOLD && + if (turbo > 100.f && cmd->ucmd.forwardmove > TURBOTHRESHOLD && !(gametic&31) && ((gametic>>5)&(MAXPLAYERS-1)) == i ) { Printf ("%s is turbo!\n", players[i].userinfo.GetName()); diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index d105ab818..b3aecb0b5 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -241,7 +241,7 @@ class CommandDrawImage : public SBarInfoCommandFlowControl applyscale = false; } if(type == PLAYERICON) - texture = TexMan[statusBar->CPlayer->mo->ScoreIcon]; + texture = TexMan(statusBar->CPlayer->mo->ScoreIcon); else if(type == AMMO1) { AAmmo *ammo = statusBar->ammo1; @@ -270,7 +270,7 @@ class CommandDrawImage : public SBarInfoCommandFlowControl { AInventory *item = statusBar->CPlayer->mo->FindInventory(); if (item != NULL) - texture = TexMan[item->Icon]; + texture = TexMan(item->Icon); } else if(type == HEXENARMOR_ARMOR || type == HEXENARMOR_SHIELD || type == HEXENARMOR_HELM || type == HEXENARMOR_AMULET) { @@ -290,7 +290,7 @@ class CommandDrawImage : public SBarInfoCommandFlowControl } } else if(type == INVENTORYICON) - texture = TexMan[sprite]; + texture = TexMan(sprite); else if(type == SELECTEDINVENTORYICON && statusBar->CPlayer->mo->InvSel != NULL) texture = TexMan(statusBar->CPlayer->mo->InvSel->Icon); else if(image >= 0) @@ -312,7 +312,7 @@ class CommandDrawImage : public SBarInfoCommandFlowControl spawnScaleY = item->Scale.Y; } - texture = TexMan[icon]; + texture = TexMan(icon); } enum ImageType @@ -2436,22 +2436,22 @@ class CommandDrawKeyBar : public SBarInfoCommand { if(!vertical) { - statusBar->DrawGraphic(TexMan[item->Icon], x+slotOffset, y+rowOffset, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); - rowWidth = rowIconSize == -1 ? TexMan[item->Icon]->GetScaledHeight()+2 : rowIconSize; + statusBar->DrawGraphic(TexMan(item->Icon), x+slotOffset, y+rowOffset, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + rowWidth = rowIconSize == -1 ? TexMan(item->Icon)->GetScaledHeight()+2 : rowIconSize; } else { - statusBar->DrawGraphic(TexMan[item->Icon], x+rowOffset, y+slotOffset, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); - rowWidth = rowIconSize == -1 ? TexMan[item->Icon]->GetScaledWidth()+2 : rowIconSize; + statusBar->DrawGraphic(TexMan(item->Icon), x+rowOffset, y+slotOffset, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + rowWidth = rowIconSize == -1 ? TexMan(item->Icon)->GetScaledWidth()+2 : rowIconSize; } // If cmd.special is -1 then the slot size is auto detected if(iconSize == -1) { if(!vertical) - slotOffset += (reverse ? -1 : 1) * (TexMan[item->Icon]->GetScaledWidth() + 2); + slotOffset += (reverse ? -1 : 1) * (TexMan(item->Icon)->GetScaledWidth() + 2); else - slotOffset += (reverse ? -1 : 1) * (TexMan[item->Icon]->GetScaledHeight() + 2); + slotOffset += (reverse ? -1 : 1) * (TexMan(item->Icon)->GetScaledHeight() + 2); } else slotOffset += (reverse ? -iconSize : iconSize); diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index a3bb76b7f..5e76b422f 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -119,6 +119,7 @@ CUSTOM_CVAR(Int, am_showmaplabel, 2, CVAR_ARCHIVE) } CVAR (Bool, idmypos, false, 0); +CVAR(Float, underwater_fade_scalar, 1.0f, CVAR_ARCHIVE) // [Nash] user-settable underwater blend intensity //--------------------------------------------------------------------------- // @@ -1544,7 +1545,10 @@ void DBaseStatusBar::DrawPowerups () void DBaseStatusBar::BlendView (float blend[4]) { - V_AddBlend (BaseBlendR / 255.f, BaseBlendG / 255.f, BaseBlendB / 255.f, BaseBlendA, blend); + // [Nash] Allow user to set blend intensity + float cnt = (BaseBlendA * underwater_fade_scalar); + + V_AddBlend (BaseBlendR / 255.f, BaseBlendG / 255.f, BaseBlendB / 255.f, cnt, blend); V_AddPlayerBlend(CPlayer, blend, 1.0f, 228); if (screen->Accel2D || (CPlayer->camera != NULL && menuactive == MENU_Off && ConsoleState == c_up)) diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 19bc93af0..6ea1e357a 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -206,7 +206,7 @@ public: void FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, - DAngle rotation, FDynamicColormap *colormap, int lightlevel); + DAngle rotation, FDynamicColormap *colormap, int lightlevel, int bottomclip); int PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num); }; diff --git a/src/gl/scene/gl_bsp.cpp b/src/gl/scene/gl_bsp.cpp index 7b9830050..f0ddfd642 100644 --- a/src/gl/scene/gl_bsp.cpp +++ b/src/gl/scene/gl_bsp.cpp @@ -140,12 +140,15 @@ static void AddLine (seg_t *seg, bool portalclip) { if (currentsector->sectornum == seg->backsector->sectornum) { - FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::mid)); - if (!tex || tex->UseType==FTexture::TEX_Null) + if (!seg->linedef->isVisualPortal()) { - // nothing to do here! - seg->linedef->validcount=validcount; - return; + FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::mid)); + if (!tex || tex->UseType==FTexture::TEX_Null) + { + // nothing to do here! + seg->linedef->validcount=validcount; + return; + } } backsector=currentsector; } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index b53be4aaa..1437d3fd5 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -82,6 +82,7 @@ CVAR(Bool, gl_sort_textures, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) EXTERN_CVAR (Bool, cl_capfps) EXTERN_CVAR (Bool, r_deathcamera) +EXTERN_CVAR (Float, underwater_fade_scalar) extern int viewpitch; @@ -602,7 +603,11 @@ void FGLRenderer::DrawBlend(sector_t * viewsector) } else if (blendv.a) { - V_AddBlend(blendv.r / 255.f, blendv.g / 255.f, blendv.b / 255.f, blendv.a / 255.0f, blend); + // [Nash] allow user to set blend intensity + int cnt = blendv.a; + cnt = (int)(cnt * underwater_fade_scalar); + + V_AddBlend(blendv.r / 255.f, blendv.g / 255.f, blendv.b / 255.f, cnt / 255.0f, blend); } } diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 6264352f6..4904cc1f6 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -464,7 +464,7 @@ void OpenGLFrameBuffer::Clear(int left, int top, int right, int bottom, int palc void OpenGLFrameBuffer::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, - DAngle rotation, FDynamicColormap *colormap, int lightlevel) + DAngle rotation, FDynamicColormap *colormap, int lightlevel, int bottomclip) { if (GLRenderer != nullptr && GLRenderer->m2DDrawer != nullptr && npoints >= 3) { diff --git a/src/gl/system/gl_framebuffer.h b/src/gl/system/gl_framebuffer.h index 5315bb0a9..27c3d1cf9 100644 --- a/src/gl/system/gl_framebuffer.h +++ b/src/gl/system/gl_framebuffer.h @@ -71,7 +71,7 @@ public: void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, - DAngle rotation, FDynamicColormap *colormap, int lightlevel); + DAngle rotation, FDynamicColormap *colormap, int lightlevel, int bottomclip); FNativePalette *CreatePalette(FRemapTable *remap); diff --git a/src/i_module.cpp b/src/i_module.cpp new file mode 100644 index 000000000..1ed40310f --- /dev/null +++ b/src/i_module.cpp @@ -0,0 +1,101 @@ +/* +** i_module.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2016 Braden Obrzut +** 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 "i_module.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define USE_WINDOWS_DWORD +#else +#include +#endif + +#ifndef _WIN32 +#define LoadLibrary(x) dlopen((x), RTLD_LAZY) +#define GetProcAddress(a,b) dlsym((a),(b)) +#define FreeLibrary(x) dlclose((x)) +using HMODULE = void*; +#endif + +bool FModule::Load(std::initializer_list libnames) +{ + for(auto lib : libnames) + { + if(!Open(lib)) + continue; + + StaticProc *proc; + for(proc = reqSymbols;proc;proc = proc->Next) + { + if(!(proc->Call = GetSym(proc->Name)) && !proc->Optional) + { + Unload(); + break; + } + } + + if(IsLoaded()) + return true; + } + + return false; +} + +void FModule::Unload() +{ + if(handle) + { + FreeLibrary((HMODULE)handle); + handle = nullptr; + } +} + +bool FModule::Open(const char* lib) +{ +#ifdef _WIN32 + if((handle = GetModuleHandle(lib)) != nullptr) + return true; +#else + // Loading an empty string in Linux doesn't do what we expect it to. + if(*lib == '\0') + return false; +#endif + handle = LoadLibrary(lib); + return handle != nullptr; +} + +void *FModule::GetSym(const char* name) +{ + return GetProcAddress((HMODULE)handle, name); +} diff --git a/src/i_module.h b/src/i_module.h new file mode 100644 index 000000000..9690d8c2f --- /dev/null +++ b/src/i_module.h @@ -0,0 +1,229 @@ +/* +** i_module.h +** +**--------------------------------------------------------------------------- +** Copyright 2016 Braden Obrzut +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#pragma once + +#include +#include + +/* FModule Run Time Library Loader + * + * This provides an interface for loading optional dependencies or detecting + * version specific symbols at run time. These classes largely provide an + * interface for statically declaring the symbols that are going to be used + * ahead of time, thus should not be used on the stack or as part of a + * dynamically allocated object. The procedure templates take the FModule + * as a template argument largely to make such use of FModule awkward. + * + * Declared procedures register themselves with FModule and the module will not + * be considered loaded unless all required procedures can be resolved. In + * order to remove the need for boilerplate code, optional procedures do not + * enforce the requirement that the value is null checked before use. As a + * debugging aid debug builds will check that operator bool was called at some + * point, but this is just a first order sanity check. + */ + +class FModule; +class FStaticModule; + +template +class TOptProc; + +template +class TReqProc; + +template +class TStaticProc; + +class FModule +{ + template + friend class TOptProc; + template + friend class TReqProc; + + struct StaticProc + { + void *Call; + const char* Name; + StaticProc *Next; + bool Optional; + }; + + void RegisterStatic(StaticProc &proc) + { + proc.Next = reqSymbols; + reqSymbols = &proc; + } + + void *handle = nullptr; + + // Debugging aid + const char *name; + + // Since FModule is supposed to be statically allocated it is assumed that + // reqSymbols will be initialized to nullptr avoiding initialization order + // problems with declaring procedures. + StaticProc *reqSymbols; + + bool Open(const char* lib); + void *GetSym(const char* name); + +public: + template + using Opt = TOptProc; + template + using Req = TReqProc; + + FModule(const char* name) : name(name) {}; + ~FModule() { Unload(); } + + // Load a shared library using the first library name which satisfies all + // of the required symbols. + bool Load(std::initializer_list libnames); + void Unload(); + + bool IsLoaded() const { return handle != nullptr; } +}; + +// Null version of FModule which satisfies the API so the same code can be used +// for run time and compile time linking. +class FStaticModule +{ + template + friend class TStaticProc; + + const char *name; +public: + template + using Opt = TStaticProc; + template + using Req = TStaticProc; + + FStaticModule(const char* name) : name(name) {}; + + bool Load(std::initializer_list libnames) { return true; } + void Unload() {} + + bool IsLoaded() const { return true; } +}; + +// Allow FModuleMaybe to switch based on preprocessor flag. +// Use FModuleMaybe::Opt and FModuleMaybe::Req for procs. +template +struct TModuleType { using Type = FModule; }; +template<> +struct TModuleType { using Type = FStaticModule; }; + +template +using FModuleMaybe = typename TModuleType::Type; + +// ------------------------------------------------------------------------ + +template +class TOptProc +{ + FModule::StaticProc proc; +#ifndef NDEBUG + mutable bool checked = false; +#endif + + // I am not a pointer + bool operator==(void*) const; + bool operator!=(void*) const; + +public: + TOptProc(const char* function) + { + proc.Name = function; + proc.Optional = true; + Module.RegisterStatic(proc); + } + + operator Proto() const + { +#ifndef NDEBUG + assert(checked); +#endif + return (Proto)proc.Call; + } + explicit operator bool() const + { +#ifndef NDEBUG + assert(Module.IsLoaded()); + checked = true; +#endif + return proc.Call != nullptr; + } +}; + +template +class TReqProc +{ + FModule::StaticProc proc; + + // I am not a pointer + bool operator==(void*) const; + bool operator!=(void*) const; + +public: + TReqProc(const char* function) + { + proc.Name = function; + proc.Optional = false; + Module.RegisterStatic(proc); + } + + operator Proto() const + { +#ifndef NDEBUG + assert(Module.IsLoaded()); +#endif + return (Proto)proc.Call; + } + explicit operator bool() const { return true; } +}; + +template +class TStaticProc +{ + // I am not a pointer + bool operator==(void*) const; + bool operator!=(void*) const; + +public: + TStaticProc(const char* function) {} + + operator Proto() const { return Sym; } + explicit operator bool() const { return Sym != nullptr; } +}; diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h index 76718809e..0fadeca37 100644 --- a/src/menu/optionmenuitems.h +++ b/src/menu/optionmenuitems.h @@ -671,6 +671,7 @@ public: { return FOptionMenuItem::MenuEvent(mkey, fromcontroller); } + if (fabs(value) < FLT_EPSILON) value = 0; SetSliderValue(clamp(value, mMin, mMax)); S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); return true; diff --git a/src/p_user.cpp b/src/p_user.cpp index 44ff21caf..10845f778 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2678,10 +2678,11 @@ void P_PlayerThink (player_t *player) // Apply degeneration. if (dmflags2 & DF2_YES_DEGENERATION) { - if ((level.time % TICRATE) == 0 && player->health > deh.MaxHealth) + int maxhealth = player->mo->GetMaxHealth() + player->mo->stamina; + if ((level.time % TICRATE) == 0 && player->health > maxhealth) { - if (player->health - 5 < deh.MaxHealth) - player->health = deh.MaxHealth; + if (player->health - 5 < maxhealth) + player->health = maxhealth; else player->health--; diff --git a/src/posix/sdl/i_main.cpp b/src/posix/sdl/i_main.cpp index 41497afe3..cb9ed5872 100644 --- a/src/posix/sdl/i_main.cpp +++ b/src/posix/sdl/i_main.cpp @@ -40,9 +40,6 @@ #include #include #include -#ifndef NO_GTK -#include -#endif #include #if defined(__MACH__) && !defined(NOASM) #include @@ -87,10 +84,6 @@ void Mac_I_FatalError(const char* errortext); // PUBLIC DATA DEFINITIONS ------------------------------------------------- -#ifndef NO_GTK -bool GtkAvailable; -#endif - // The command line arguments. DArgs *Args; @@ -259,10 +252,6 @@ int main (int argc, char **argv) // Note that the LANG environment variable is overridden by LC_* setenv ("LC_NUMERIC", "C", 1); -#ifndef NO_GTK - GtkAvailable = gtk_init_check (&argc, &argv); -#endif - setlocale (LC_ALL, "C"); if (SDL_Init (0) < 0) diff --git a/src/posix/sdl/i_system.cpp b/src/posix/sdl/i_system.cpp index 73485f309..50a07af05 100644 --- a/src/posix/sdl/i_system.cpp +++ b/src/posix/sdl/i_system.cpp @@ -35,10 +35,8 @@ #include #include #include -#ifndef NO_GTK -#include -#include -#endif + +#include #include "doomerrors.h" #include @@ -72,10 +70,6 @@ #include "m_fixed.h" #include "g_level.h" -#ifdef __APPLE__ -#include -#endif // __APPLE__ - EXTERN_CVAR (String, language) extern "C" @@ -85,7 +79,8 @@ extern "C" } #ifndef NO_GTK -extern bool GtkAvailable; +bool I_GtkAvailable (); +int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad); #elif defined(__APPLE__) int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad); #endif @@ -263,183 +258,6 @@ void I_PrintStr (const char *cp) fflush (stdout); } -#ifndef NO_GTK -// GtkTreeViews eats return keys. I want this to be like a Windows listbox -// where pressing Return can still activate the default button. -gint AllowDefault(GtkWidget *widget, GdkEventKey *event, gpointer func_data) -{ - if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Return) - { - gtk_window_activate_default (GTK_WINDOW(func_data)); - } - return FALSE; -} - -// Double-clicking an entry in the list is the same as pressing OK. -gint DoubleClickChecker(GtkWidget *widget, GdkEventButton *event, gpointer func_data) -{ - if (event->type == GDK_2BUTTON_PRESS) - { - *(int *)func_data = 1; - gtk_main_quit(); - } - return FALSE; -} - -// When the user presses escape, that should be the same as canceling the dialog. -gint CheckEscape (GtkWidget *widget, GdkEventKey *event, gpointer func_data) -{ - if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Escape) - { - gtk_main_quit(); - } - return FALSE; -} - -void ClickedOK(GtkButton *button, gpointer func_data) -{ - *(int *)func_data = 1; - gtk_main_quit(); -} - -EXTERN_CVAR (Bool, queryiwad); - -int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad) -{ - GtkWidget *window; - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *bbox; - GtkWidget *widget; - GtkWidget *tree; - GtkWidget *check; - GtkListStore *store; - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GtkTreeSelection *selection; - GtkTreeIter iter, defiter; - int close_style = 0; - int i; - char caption[100]; - - // Create the dialog window. - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - mysnprintf(caption, countof(caption), GAMESIG " %s: Select an IWAD to use", GetVersionString()); - gtk_window_set_title (GTK_WINDOW(window), caption); - gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER); - gtk_container_set_border_width (GTK_CONTAINER(window), 10); - g_signal_connect (window, "delete_event", G_CALLBACK(gtk_main_quit), NULL); - g_signal_connect (window, "key_press_event", G_CALLBACK(CheckEscape), NULL); - - // Create the vbox container. - vbox = gtk_vbox_new (FALSE, 10); - gtk_container_add (GTK_CONTAINER(window), vbox); - - // Create the top label. - widget = gtk_label_new (GAMENAME " found more than one IWAD\nSelect from the list below to determine which one to use:"); - gtk_box_pack_start (GTK_BOX(vbox), widget, false, false, 0); - gtk_misc_set_alignment (GTK_MISC(widget), 0, 0); - - // Create a list store with all the found IWADs. - store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); - for (i = 0; i < numwads; ++i) - { - const char *filepart = strrchr (wads[i].Path, '/'); - if (filepart == NULL) - filepart = wads[i].Path; - else - filepart++; - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, - 0, filepart, - 1, wads[i].Name.GetChars(), - 2, i, - -1); - if (i == defaultiwad) - { - defiter = iter; - } - } - - // Create the tree view control to show the list. - tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store)); - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("IWAD", renderer, "text", 0, NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column); - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("Game", renderer, "text", 1, NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column); - gtk_box_pack_start (GTK_BOX(vbox), GTK_WIDGET(tree), true, true, 0); - g_signal_connect(G_OBJECT(tree), "button_press_event", G_CALLBACK(DoubleClickChecker), &close_style); - g_signal_connect(G_OBJECT(tree), "key_press_event", G_CALLBACK(AllowDefault), window); - - // Select the default IWAD. - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree)); - gtk_tree_selection_select_iter (selection, &defiter); - - // Create the hbox for the bottom row. - hbox = gtk_hbox_new (FALSE, 0); - gtk_box_pack_end (GTK_BOX(vbox), hbox, false, false, 0); - - // Create the "Don't ask" checkbox. - check = gtk_check_button_new_with_label ("Don't ask me this again"); - gtk_box_pack_start (GTK_BOX(hbox), check, false, false, 0); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(check), !showwin); - - // Create the OK/Cancel button box. - bbox = gtk_hbutton_box_new (); - gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_set_spacing (GTK_BOX(bbox), 10); - gtk_box_pack_end (GTK_BOX(hbox), bbox, false, false, 0); - - // Create the OK button. - widget = gtk_button_new_from_stock (GTK_STOCK_OK); - gtk_box_pack_start (GTK_BOX(bbox), widget, false, false, 0); - GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT); - gtk_widget_grab_default (widget); - g_signal_connect (widget, "clicked", G_CALLBACK(ClickedOK), &close_style); - g_signal_connect (widget, "activate", G_CALLBACK(ClickedOK), &close_style); - - // Create the cancel button. - widget = gtk_button_new_from_stock (GTK_STOCK_CANCEL); - gtk_box_pack_start (GTK_BOX(bbox), widget, false, false, 0); - g_signal_connect (widget, "clicked", G_CALLBACK(gtk_main_quit), &window); - - // Finally we can show everything. - gtk_widget_show_all (window); - - gtk_main (); - - if (close_style == 1) - { - GtkTreeModel *model; - GValue value = { 0, { {0} } }; - - // Find out which IWAD was selected. - gtk_tree_selection_get_selected (selection, &model, &iter); - gtk_tree_model_get_value (GTK_TREE_MODEL(model), &iter, 2, &value); - i = g_value_get_int (&value); - g_value_unset (&value); - - // Set state of queryiwad based on the checkbox. - queryiwad = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(check)); - } - else - { - i = -1; - } - - if (GTK_IS_WINDOW(window)) - { - gtk_widget_destroy (window); - // If we don't do this, then the X window might not actually disappear. - while (g_main_context_iteration (NULL, FALSE)) {} - } - - return i; -} -#endif - int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) { int i; @@ -449,7 +267,7 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) return defaultiwad; } -#if !defined(__APPLE__) +#ifndef __APPLE__ const char *str; if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0) { @@ -501,12 +319,15 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) } } #endif + #ifndef NO_GTK - if (GtkAvailable) + if (I_GtkAvailable()) { return I_PickIWad_Gtk (wads, numwads, showwin, defaultiwad); } -#elif defined(__APPLE__) +#endif + +#ifdef __APPLE__ return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad); #endif @@ -606,139 +427,19 @@ int I_FindAttr (findstate_t *fileinfo) return 0; } -#ifdef __APPLE__ -static PasteboardRef s_clipboard; - -static CFDataRef GetPasteboardData(const PasteboardItemID itemID, const CFStringRef flavorType) -{ - CFDataRef data = NULL; - - const OSStatus result = PasteboardCopyItemFlavorData(s_clipboard, itemID, flavorType, &data); - - return noErr == result ? data : NULL; -} -#endif // __APPLE__ - -// Clipboard support requires GTK+ -// TODO: GTK+ uses UTF-8. We don't, so some conversions would be appropriate. void I_PutInClipboard (const char *str) { -#ifndef NO_GTK - if (GtkAvailable) - { - GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); - if (clipboard != NULL) - { - gtk_clipboard_set_text(clipboard, str, -1); - } - /* Should I? I don't know. It's not actually a selection. - clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); - if (clipboard != NULL) - { - gtk_clipboard_set_text(clipboard, str, -1); - } - */ - } -#elif defined __APPLE__ - if (NULL == s_clipboard) - { - PasteboardCreate(kPasteboardClipboard, &s_clipboard); - } - - PasteboardClear(s_clipboard); - PasteboardSynchronize(s_clipboard); - - const CFDataRef textData = CFDataCreate(kCFAllocatorDefault, - reinterpret_cast(str), strlen(str)); - - if (NULL != textData) - { - PasteboardPutItemFlavor(s_clipboard, PasteboardItemID(1), - CFSTR("public.utf8-plain-text"), textData, 0); - } -#endif + SDL_SetClipboardText(str); } FString I_GetFromClipboard (bool use_primary_selection) { -#ifndef NO_GTK - if (GtkAvailable) + if(char *ret = SDL_GetClipboardText()) { - GtkClipboard *clipboard = gtk_clipboard_get(use_primary_selection ? - GDK_SELECTION_PRIMARY : GDK_SELECTION_CLIPBOARD); - if (clipboard != NULL) - { - gchar *text = gtk_clipboard_wait_for_text(clipboard); - if (text != NULL) - { - FString copy(text); - g_free(text); - return copy; - } - } + FString text(ret); + SDL_free(ret); + return text; } -#elif defined __APPLE__ - FString result; - - if (NULL == s_clipboard) - { - PasteboardCreate(kPasteboardClipboard, &s_clipboard); - } - - PasteboardSynchronize(s_clipboard); - - ItemCount itemCount = 0; - PasteboardGetItemCount(s_clipboard, &itemCount); - - if (0 == itemCount) - { - return FString(); - } - - PasteboardItemID itemID; - - if (0 != PasteboardGetItemIdentifier(s_clipboard, 1, &itemID)) - { - return FString(); - } - - if (CFDataRef data = GetPasteboardData(itemID, kUTTypeUTF8PlainText)) - { - const CFIndex bufferLength = CFDataGetLength(data); - char* const buffer = result.LockNewBuffer(bufferLength); - - memcpy(buffer, CFDataGetBytePtr(data), bufferLength); - - result.UnlockBuffer(); - } - else if (CFDataRef data = GetPasteboardData(itemID, kUTTypeUTF16PlainText)) - { -#ifdef __LITTLE_ENDIAN__ - static const CFStringEncoding ENCODING = kCFStringEncodingUTF16LE; -#else // __BIG_ENDIAN__ - static const CFStringEncoding ENCODING = kCFStringEncodingUTF16BE; -#endif // __LITTLE_ENDIAN__ - - if (const CFStringRef utf16 = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, data, ENCODING)) - { - const CFRange range = { 0, CFStringGetLength(utf16) }; - CFIndex bufferLength = 0; - - if (CFStringGetBytes(utf16, range, kCFStringEncodingUTF8, '?', false, NULL, 0, &bufferLength) > 0) - { - UInt8* const buffer = reinterpret_cast(result.LockNewBuffer(bufferLength)); - - CFStringGetBytes(utf16, range, kCFStringEncodingUTF8, '?', false, buffer, bufferLength, NULL); - - result.UnlockBuffer(); - } - - CFRelease(utf16); - } - } - - return result; -#endif return ""; } diff --git a/src/posix/unix/iwadpicker_gtk.cpp b/src/posix/unix/iwadpicker_gtk.cpp new file mode 100644 index 000000000..56228c4e8 --- /dev/null +++ b/src/posix/unix/iwadpicker_gtk.cpp @@ -0,0 +1,339 @@ +#ifndef NO_GTK + +#if !DYN_GTK +// Function addresses will never be NULL, but that's because we're using the +// same code for both dynamic and static. +#pragma GCC diagnostic ignored "-Waddress" +#endif + +#include +#if GTK_MAJOR_VERSION >= 3 +#include +#else +#include +typedef enum +{ + GTK_ALIGN_FULL, + GTK_ALIGN_START, + GTK_ALIGN_END, + GTK_ALIGN_CENTER, + GTK_ALIGN_BASELINE +} GtkAlign; +#endif + +#include "c_cvars.h" +#include "d_main.h" +#include "i_module.h" +#include "i_system.h" +#include "version.h" + +EXTERN_CVAR (Bool, queryiwad); + +namespace Gtk { + +FModuleMaybe GtkModule{"GTK"}; +static int GtkAvailable = -1; + +#define DYN_GTK_SYM(x) const FModuleMaybe::Req x{#x}; +#define DYN_GTK_REQ_SYM(x, proto) const FModuleMaybe::Req x{#x}; +#if GTK_MAJOR_VERSION >= 3 +#define DYN_GTK_OPT2_SYM(x, proto) const FModuleMaybe::Opt x{#x}; +#define DYN_GTK_OPT3_SYM(x, proto) const FModuleMaybe::Opt x{#x}; +#else +#define DYN_GTK_OPT2_SYM(x, proto) const FModuleMaybe::Opt x{#x}; +#define DYN_GTK_OPT3_SYM(x, proto) const FModuleMaybe::Opt x{#x}; +#endif + +DYN_GTK_SYM(g_main_context_iteration); +DYN_GTK_SYM(g_signal_connect_data); +DYN_GTK_SYM(g_type_check_instance_cast); +DYN_GTK_SYM(g_type_check_instance_is_a); +DYN_GTK_SYM(g_value_get_int); +DYN_GTK_SYM(g_value_unset); +DYN_GTK_SYM(gtk_box_get_type); +DYN_GTK_SYM(gtk_box_pack_end); +DYN_GTK_SYM(gtk_box_pack_start); +DYN_GTK_SYM(gtk_box_set_spacing); +DYN_GTK_SYM(gtk_button_box_get_type); +DYN_GTK_SYM(gtk_button_box_set_layout); +DYN_GTK_SYM(gtk_button_new_with_label); +DYN_GTK_SYM(gtk_cell_renderer_text_new); +DYN_GTK_SYM(gtk_check_button_new_with_label); +DYN_GTK_SYM(gtk_container_add); +DYN_GTK_SYM(gtk_container_get_type); +DYN_GTK_SYM(gtk_container_set_border_width); +DYN_GTK_SYM(gtk_init_check); +DYN_GTK_SYM(gtk_label_new); +DYN_GTK_SYM(gtk_list_store_append); +DYN_GTK_SYM(gtk_list_store_new); +DYN_GTK_SYM(gtk_list_store_set); +DYN_GTK_SYM(gtk_toggle_button_get_type); +DYN_GTK_SYM(gtk_toggle_button_set_active); +DYN_GTK_SYM(gtk_tree_model_get_type); +DYN_GTK_SYM(gtk_tree_model_get_value); +DYN_GTK_SYM(gtk_tree_selection_get_selected); +DYN_GTK_SYM(gtk_tree_selection_select_iter); +DYN_GTK_SYM(gtk_tree_view_append_column); +// Explicitly give the type so that attributes don't cause a warning. +DYN_GTK_REQ_SYM(gtk_tree_view_column_new_with_attributes, GtkTreeViewColumn *(*)(const gchar *, GtkCellRenderer *, ...)); +DYN_GTK_SYM(gtk_toggle_button_get_active); +DYN_GTK_SYM(gtk_tree_view_get_selection); +DYN_GTK_SYM(gtk_tree_view_get_type); +DYN_GTK_SYM(gtk_tree_view_new_with_model); +DYN_GTK_SYM(gtk_main); +DYN_GTK_SYM(gtk_main_quit); +DYN_GTK_SYM(gtk_widget_destroy); +DYN_GTK_SYM(gtk_widget_grab_default); +DYN_GTK_SYM(gtk_widget_get_type); +DYN_GTK_SYM(gtk_widget_set_can_default); +DYN_GTK_SYM(gtk_widget_show_all); +DYN_GTK_SYM(gtk_window_activate_default); +DYN_GTK_SYM(gtk_window_get_type); +DYN_GTK_SYM(gtk_window_new); +DYN_GTK_SYM(gtk_window_set_gravity); +DYN_GTK_SYM(gtk_window_set_position); +DYN_GTK_SYM(gtk_window_set_title); + +// Gtk3 Only +DYN_GTK_OPT3_SYM(gtk_box_new, GtkWidget *(*)(GtkOrientation, gint)); +DYN_GTK_OPT3_SYM(gtk_button_box_new, GtkWidget *(*)(GtkOrientation)); +DYN_GTK_OPT3_SYM(gtk_widget_set_halign, void(*)(GtkWidget *, GtkAlign)); +DYN_GTK_OPT3_SYM(gtk_widget_set_valign, void(*)(GtkWidget *, GtkAlign)); + +// Gtk2 Only +DYN_GTK_OPT2_SYM(gtk_misc_get_type, GType(*)()); +DYN_GTK_OPT2_SYM(gtk_hbox_new, GtkWidget *(*)(gboolean, gint)); +DYN_GTK_OPT2_SYM(gtk_hbutton_box_new, GtkWidget *(*)()); +DYN_GTK_OPT2_SYM(gtk_misc_set_alignment, void(*)(GtkMisc *, gfloat, gfloat)); +DYN_GTK_OPT2_SYM(gtk_vbox_new, GtkWidget *(*)(gboolean, gint)); + +#undef DYN_GTK_SYM +#undef DYN_GTK_REQ_SYM +#undef DYN_GTK_OPT2_SYM +#undef DYN_GTK_OPT3_SYM + +// GtkTreeViews eats return keys. I want this to be like a Windows listbox +// where pressing Return can still activate the default button. +static gint AllowDefault(GtkWidget *widget, GdkEventKey *event, gpointer func_data) +{ + if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KEY_Return) + { + gtk_window_activate_default (GTK_WINDOW(func_data)); + } + return FALSE; +} + +// Double-clicking an entry in the list is the same as pressing OK. +static gint DoubleClickChecker(GtkWidget *widget, GdkEventButton *event, gpointer func_data) +{ + if (event->type == GDK_2BUTTON_PRESS) + { + *(int *)func_data = 1; + gtk_main_quit(); + } + return FALSE; +} + +// When the user presses escape, that should be the same as canceling the dialog. +static gint CheckEscape (GtkWidget *widget, GdkEventKey *event, gpointer func_data) +{ + if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KEY_Escape) + { + gtk_main_quit(); + } + return FALSE; +} + +static void ClickedOK(GtkButton *button, gpointer func_data) +{ + *(int *)func_data = 1; + gtk_main_quit(); +} + +static int PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) +{ + GtkWidget *window; + GtkWidget *vbox = nullptr; + GtkWidget *hbox = nullptr; + GtkWidget *bbox = nullptr; + GtkWidget *widget; + GtkWidget *tree; + GtkWidget *check; + GtkListStore *store; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + GtkTreeIter iter, defiter; + int close_style = 0; + int i; + char caption[100]; + + // Create the dialog window. + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + mysnprintf(caption, countof(caption), GAMESIG " %s: Select an IWAD to use", GetVersionString()); + gtk_window_set_title (GTK_WINDOW(window), caption); + gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_gravity (GTK_WINDOW(window), GDK_GRAVITY_CENTER); + gtk_container_set_border_width (GTK_CONTAINER(window), 10); + g_signal_connect (window, "delete_event", G_CALLBACK(gtk_main_quit), NULL); + g_signal_connect (window, "key_press_event", G_CALLBACK(CheckEscape), NULL); + + // Create the vbox container. + if (gtk_box_new) // Gtk3 + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); + else if (gtk_vbox_new) // Gtk2 + vbox = gtk_vbox_new (FALSE, 10); + + gtk_container_add (GTK_CONTAINER(window), vbox); + + // Create the top label. + widget = gtk_label_new (GAMENAME " found more than one IWAD\nSelect from the list below to determine which one to use:"); + gtk_box_pack_start (GTK_BOX(vbox), widget, false, false, 0); + + if (gtk_widget_set_halign && gtk_widget_set_valign) // Gtk3 + { + gtk_widget_set_halign (widget, GTK_ALIGN_START); + gtk_widget_set_valign (widget, GTK_ALIGN_START); + } + else if (gtk_misc_set_alignment && gtk_misc_get_type) // Gtk2 + gtk_misc_set_alignment (GTK_MISC(widget), 0, 0); + + // Create a list store with all the found IWADs. + store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); + for (i = 0; i < numwads; ++i) + { + const char *filepart = strrchr (wads[i].Path, '/'); + if (filepart == NULL) + filepart = wads[i].Path; + else + filepart++; + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, filepart, + 1, wads[i].Name.GetChars(), + 2, i, + -1); + if (i == defaultiwad) + { + defiter = iter; + } + } + + // Create the tree view control to show the list. + tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store)); + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("IWAD", renderer, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column); + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Game", renderer, "text", 1, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column); + gtk_box_pack_start (GTK_BOX(vbox), GTK_WIDGET(tree), true, true, 0); + g_signal_connect(G_OBJECT(tree), "button_press_event", G_CALLBACK(DoubleClickChecker), &close_style); + g_signal_connect(G_OBJECT(tree), "key_press_event", G_CALLBACK(AllowDefault), window); + + // Select the default IWAD. + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree)); + gtk_tree_selection_select_iter (selection, &defiter); + + // Create the hbox for the bottom row. + if (gtk_box_new) // Gtk3 + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + else if (gtk_hbox_new) // Gtk2 + hbox = gtk_hbox_new (FALSE, 0); + + gtk_box_pack_end (GTK_BOX(vbox), hbox, false, false, 0); + + // Create the "Don't ask" checkbox. + check = gtk_check_button_new_with_label ("Don't ask me this again"); + gtk_box_pack_start (GTK_BOX(hbox), check, false, false, 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(check), !showwin); + + // Create the OK/Cancel button box. + if (gtk_button_box_new) // Gtk3 + bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + else if (gtk_hbutton_box_new) // Gtk2 + bbox = gtk_hbutton_box_new (); + + gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX(bbox), 10); + gtk_box_pack_end (GTK_BOX(hbox), bbox, false, false, 0); + + // Create the OK button. + widget = gtk_button_new_with_label ("OK"); + + gtk_box_pack_start (GTK_BOX(bbox), widget, false, false, 0); + + gtk_widget_set_can_default (widget, true); + + gtk_widget_grab_default (widget); + g_signal_connect (widget, "clicked", G_CALLBACK(ClickedOK), &close_style); + g_signal_connect (widget, "activate", G_CALLBACK(ClickedOK), &close_style); + + // Create the cancel button. + widget = gtk_button_new_with_label ("Cancel"); + + gtk_box_pack_start (GTK_BOX(bbox), widget, false, false, 0); + g_signal_connect (widget, "clicked", G_CALLBACK(gtk_main_quit), &window); + + // Finally we can show everything. + gtk_widget_show_all (window); + + gtk_main (); + + if (close_style == 1) + { + GtkTreeModel *model; + GValue value = { 0, { {0} } }; + + // Find out which IWAD was selected. + gtk_tree_selection_get_selected (selection, &model, &iter); + gtk_tree_model_get_value (GTK_TREE_MODEL(model), &iter, 2, &value); + i = g_value_get_int (&value); + g_value_unset (&value); + + // Set state of queryiwad based on the checkbox. + queryiwad = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(check)); + } + else + { + i = -1; + } + + if (GTK_IS_WINDOW(window)) + { + gtk_widget_destroy (window); + // If we don't do this, then the X window might not actually disappear. + while (g_main_context_iteration (NULL, FALSE)) {} + } + + return i; +} + +} // namespace Gtk + +int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad) +{ + return Gtk::PickIWad (wads, numwads, showwin, defaultiwad); +} + +bool I_GtkAvailable() +{ + using namespace Gtk; + + if(GtkAvailable < 0) + { + if (!GtkModule.Load({"libgtk-3.so.0", "libgtk-x11-2.0.so.0"})) + { + GtkAvailable = 0; + return false; + } + + int argc = 0; + char **argv = nullptr; + GtkAvailable = Gtk::gtk_init_check (&argc, &argv); + } + + return GtkAvailable != 0; +} + +#endif diff --git a/src/r_segs.cpp b/src/r_segs.cpp index 45e3044d3..369722242 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -1066,56 +1066,188 @@ void R_RenderFakeWallRange (drawseg_t *ds, int x1, int x2) return; } -// prevlineasm1 is like vlineasm1 but skips the loop if only drawing one pixel -inline fixed_t prevline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) +struct WallscanSampler { - dc_iscale = vince; - dc_colormap = colormap; - dc_count = count; - dc_texturefrac = vplce; - dc_source = bufplce; - dc_dest = dest; - return doprevline1 (); -} + WallscanSampler() { } + WallscanSampler(int y1, float swal, double yrepeat, fixed_t xoffset, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)); -void wallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, - double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) + uint32_t uv_pos; + uint32_t uv_step; + uint32_t uv_max; + + const BYTE *source; + uint32_t height; +}; + +WallscanSampler::WallscanSampler(int y1, float swal, double yrepeat, fixed_t xoffset, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)) { - int x, fracbits; - int y1ve[4], y2ve[4], u4, d4, z; - char bad; - float light = rw_light - rw_lightstep; - SDWORD xoffset; - BYTE *basecolormapdata; - double iscale; + height = texture->GetHeight(); - // This function also gets used to draw skies. Unlike BUILD, skies are - // drawn by visplane instead of by bunch, so these checks are invalid. - //if ((uwal[x1] > viewheight) && (uwal[x2] > viewheight)) return; - //if ((dwal[x1] < 0) && (dwal[x2] < 0)) return; - - if (rw_pic->UseType == FTexture::TEX_Null) + int uv_fracbits = 32 - texture->HeightBits; + if (uv_fracbits != 32) { - return; + uv_max = height << uv_fracbits; + + // Find start uv in [0-base_height[ range. + // Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range. + double uv_stepd = swal * yrepeat; + double v = (dc_texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; + v = v - floor(v); + v *= height; + v *= (1 << uv_fracbits); + + uv_pos = (uint32_t)v; + uv_step = xs_ToFixed(uv_fracbits, uv_stepd); + if (uv_step == 0) // To prevent divide by zero elsewhere + uv_step = 1; + } + else + { // Hack for one pixel tall textures + uv_pos = 0; + uv_step = 0; + uv_max = 1; } -//extern cycle_t WallScanCycles; -//clock (WallScanCycles); + source = getcol(texture, xoffset >> FRACBITS); +} - rw_pic->GetHeight(); // Make sure texture size is loaded - fracbits = 32 - rw_pic->HeightBits; +// Draw a column with support for non-power-of-two ranges +void wallscan_drawcol1(int x, int y1, int y2, WallscanSampler &sampler, DWORD(*draw1column)()) +{ + if (sampler.uv_max == 0 || sampler.uv_step == 0) // power of two + { + int count = y2 - y1; + + dc_source = sampler.source; + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + dc_iscale = sampler.uv_step; + dc_texturefrac = sampler.uv_pos; + draw1column(); + + uint64_t step64 = sampler.uv_step; + uint64_t pos64 = sampler.uv_pos; + sampler.uv_pos = (uint32_t)(pos64 + step64 * count); + } + else + { + uint32_t uv_pos = sampler.uv_pos; + + uint32_t left = y2 - y1; + while (left > 0) + { + uint32_t available = sampler.uv_max - uv_pos; + uint32_t next_uv_wrap = available / sampler.uv_step; + if (available % sampler.uv_step != 0) + next_uv_wrap++; + uint32_t count = MIN(left, next_uv_wrap); + + dc_source = sampler.source; + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + dc_iscale = sampler.uv_step; + dc_texturefrac = uv_pos; + draw1column(); + + left -= count; + uv_pos += sampler.uv_step * count; + if (uv_pos >= sampler.uv_max) + uv_pos -= sampler.uv_max; + } + + sampler.uv_pos = uv_pos; + } +} + +// Draw four columns with support for non-power-of-two ranges +void wallscan_drawcol4(int x, int y1, int y2, WallscanSampler *sampler, void(*draw4columns)()) +{ + if (sampler[0].uv_max == 0 || sampler[0].uv_step == 0) // power of two, no wrap handling needed + { + int count = y2 - y1; + for (int i = 0; i < 4; i++) + { + bufplce[i] = sampler[i].source; + vplce[i] = sampler[i].uv_pos; + vince[i] = sampler[i].uv_step; + + uint64_t step64 = sampler[i].uv_step; + uint64_t pos64 = sampler[i].uv_pos; + sampler[i].uv_pos = (uint32_t)(pos64 + step64 * count); + } + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + draw4columns(); + } + else + { + dc_dest = (ylookup[y1] + x) + dc_destorg; + for (int i = 0; i < 4; i++) + { + bufplce[i] = sampler[i].source; + } + + uint32_t left = y2 - y1; + while (left > 0) + { + // Find which column wraps first + uint32_t count = left; + for (int i = 0; i < 4; i++) + { + uint32_t available = sampler[i].uv_max - sampler[i].uv_pos; + uint32_t next_uv_wrap = available / sampler[i].uv_step; + if (available % sampler[i].uv_step != 0) + next_uv_wrap++; + count = MIN(next_uv_wrap, count); + } + + // Draw until that column wraps + for (int i = 0; i < 4; i++) + { + vplce[i] = sampler[i].uv_pos; + vince[i] = sampler[i].uv_step; + } + dc_count = count; + draw4columns(); + + // Wrap the uv position + for (int i = 0; i < 4; i++) + { + sampler[i].uv_pos += sampler[i].uv_step * count; + if (sampler[i].uv_pos >= sampler[i].uv_max) + sampler[i].uv_pos -= sampler[i].uv_max; + } + + left -= count; + } + } +} + +typedef DWORD(*Draw1ColumnFuncPtr)(); +typedef void(*Draw4ColumnsFuncPtr)(); + +void wallscan_any( + int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, + const BYTE *(*getcol)(FTexture *tex, int x), + void(setupwallscan(int bits, Draw1ColumnFuncPtr &draw1, Draw4ColumnsFuncPtr &draw2))) +{ + if (rw_pic->UseType == FTexture::TEX_Null) + return; + + fixed_t xoffset = rw_offset; + + rw_pic->GetHeight(); // To ensure that rw_pic->HeightBits has been set + int fracbits = 32 - rw_pic->HeightBits; if (fracbits == 32) { // Hack for one pixel tall textures fracbits = 0; yrepeat = 0; dc_texturemid = 0; } - setupvline(fracbits); - xoffset = rw_offset; - basecolormapdata = basecolormap->Maps; - x = x1; - //while ((umost[x] > dmost[x]) && (x < x2)) x++; + DWORD(*draw1column)(); + void(*draw4columns)(); + setupwallscan(fracbits, draw1column, draw4columns); bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); if (fixed) @@ -1126,128 +1258,175 @@ void wallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *l palookupoffse[3] = dc_colormap; } - for(; (x < x2) && (x & 3); ++x) + if (fixedcolormap) + dc_colormap = fixedcolormap; + else + dc_colormap = basecolormap->Maps; + + float light = rw_light; + + // Calculate where 4 column alignment begins and ends: + int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); + int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); + + // First unaligned columns: + for (int x = x1; x < aligned_x1; x++, light += rw_lightstep) { - light += rw_lightstep; - y1ve[0] = uwal[x];//max(uwal[x],umost[x]); - y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); - if (y2ve[0] <= y1ve[0]) continue; - assert (y1ve[0] < viewheight); - assert (y2ve[0] <= viewheight); + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } + dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + x + dc_destorg; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 0.5)); - - dovline1(); + WallscanSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, rw_pic, getcol); + wallscan_drawcol1(x, y1, y2, sampler, draw1column); } - for(; x < x2-3; x += 4) + // The aligned columns + for (int x = aligned_x1; x < aligned_x2; x += 4) { - bad = 0; - for (z = 3; z>= 0; --z) - { - y1ve[z] = uwal[x+z];//max(uwal[x+z],umost[x+z]); - y2ve[z] = dwal[x+z];//min(dwal[x+z],dmost[x+z])-1; - if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); - iscale = swal[x + z] * yrepeat; - vince[z] = xs_ToFixed(fracbits, iscale); - vplce[z] = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[z] - CenterY + 0.5)); - } - if (bad == 15) + float lights[4]; + for (int i = 0; i < 4; i++) { - light += rw_lightstep * 4; - continue; + lights[i] = light; + light += rw_lightstep; } - if (!fixed) + WallscanSampler sampler[4]; + for (int i = 0; i < 4; i++) + sampler[i] = WallscanSampler(y1[i], swal[x + i], yrepeat, lwal[x + i] + xoffset, rw_pic, getcol); + + // Figure out where we vertically can start and stop drawing 4 columns in one go + int middle_y1 = y1[0]; + int middle_y2 = y2[0]; + for (int i = 1; i < 4; i++) { - for (z = 0; z < 4; ++z) + middle_y1 = MAX(y1[i], middle_y1); + middle_y2 = MIN(y2[i], middle_y2); + } + + // If we got an empty column in our set we cannot draw 4 columns in one go: + bool empty_column_in_set = false; + for (int i = 0; i < 4; i++) + { + if (y2[i] <= y1[i]) + empty_column_in_set = true; + } + + if (empty_column_in_set || middle_y2 <= middle_y1) + { + for (int i = 0; i < 4; i++) { - light += rw_lightstep; - palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - } + if (y2[i] <= y1[i]) + continue; - u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); - d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); - - if ((bad != 0) || (u4 >= d4)) - { - for (z = 0; z < 4; ++z) - { - if (!(bad & 1)) - { - prevline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+x+z+dc_destorg); - } - bad >>= 1; + if (!fixed) + dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); + wallscan_drawcol1(x + i, y1[i], y2[i], sampler[i], draw1column); } continue; } - for (z = 0; z < 4; ++z) + // Draw the first rows where not all 4 columns are active + for (int i = 0; i < 4; i++) { - if (u4 > y1ve[z]) - { - vplce[z] = prevline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+x+z+dc_destorg); - } + if (!fixed) + dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); + + if (y1[i] < middle_y1) + wallscan_drawcol1(x + i, y1[i], middle_y1, sampler[i], draw1column); } - if (d4 > u4) + // Draw the area where all 4 columns are active + if (!fixed) { - dc_count = d4-u4; - dc_dest = ylookup[u4]+x+dc_destorg; - dovline4(); - } - - BYTE *i = x+ylookup[d4]+dc_destorg; - for (z = 0; z < 4; ++z) - { - if (y2ve[z] > d4) + for (int i = 0; i < 4; i++) { - prevline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); + palookupoffse[i] = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); } } + wallscan_drawcol4(x, middle_y1, middle_y2, sampler, draw4columns); + + // Draw the last rows where not all 4 columns are active + for (int i = 0; i < 4; i++) + { + if (!fixed) + dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); + + if (middle_y2 < y2[i]) + wallscan_drawcol1(x + i, middle_y2, y2[i], sampler[i], draw1column); + } } - for(;xMaps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + x + dc_destorg; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 0.5)); - - dovline1(); + WallscanSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, rw_pic, getcol); + wallscan_drawcol1(x, y1, y2, sampler, draw1column); } -//unclock (WallScanCycles); + NetUpdate(); +} - NetUpdate (); +void wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) +{ + wallscan_any(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setupvline(bits); + line1 = dovline1; + line4 = dovline4; + }); +} + +void maskwallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) +{ + if (!rw_pic->bMasked) // Textures that aren't masked can use the faster wallscan. + { + wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); + } + else + { + wallscan_any(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setupmvline(bits); + line1 = domvline1; + line4 = domvline4; + }); + } +} + +void transmaskwallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) +{ + static fixed_t(*tmvline1)(); + static void(*tmvline4)(); + if (!R_GetTransMaskDrawers(&tmvline1, &tmvline4)) + { + // The current translucency is unsupported, so draw with regular maskwallscan instead. + maskwallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); + } + else + { + wallscan_any(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setuptmvline(bits); + line1 = reinterpret_cast(tmvline1); + line4 = tmvline4; + }); + } } void wallscan_striped (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) @@ -1423,370 +1602,6 @@ static void wallscan_np2_ds(drawseg_t *ds, int x1, int x2, short *uwal, short *d } } -inline fixed_t mvline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) -{ - dc_iscale = vince; - dc_colormap = colormap; - dc_count = count; - dc_texturefrac = vplce; - dc_source = bufplce; - dc_dest = dest; - return domvline1 (); -} - -void maskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, - double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - int x, fracbits; - BYTE *p; - int y1ve[4], y2ve[4], u4, d4, startx, dax, z; - char bad; - float light = rw_light - rw_lightstep; - SDWORD xoffset; - BYTE *basecolormapdata; - double iscale; - - if (rw_pic->UseType == FTexture::TEX_Null) - { - return; - } - - if (!rw_pic->bMasked) - { // Textures that aren't masked can use the faster wallscan. - wallscan (x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - return; - } - -//extern cycle_t WallScanCycles; -//clock (WallScanCycles); - - rw_pic->GetHeight(); // Make sure texture size is loaded - fracbits = 32- rw_pic->HeightBits; - if (fracbits == 32) - { // Hack for one pixel tall textures - fracbits = 0; - yrepeat = 0; - dc_texturemid = 0; - } - setupmvline(fracbits); - xoffset = rw_offset; - basecolormapdata = basecolormap->Maps; - - x = startx = x1; - p = x + dc_destorg; - - bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); - if (fixed) - { - palookupoffse[0] = dc_colormap; - palookupoffse[1] = dc_colormap; - palookupoffse[2] = dc_colormap; - palookupoffse[3] = dc_colormap; - } - - for(; (x < x2) && ((size_t)p & 3); ++x, ++p) - { - light += rw_lightstep; - y1ve[0] = uwal[x];//max(uwal[x],umost[x]); - y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); - if (y2ve[0] <= y1ve[0]) continue; - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + p; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 0.5)); - - domvline1(); - } - - for(; x < x2-3; x += 4, p+= 4) - { - bad = 0; - for (z = 3, dax = x+3; z >= 0; --z, --dax) - { - y1ve[z] = uwal[dax]; - y2ve[z] = dwal[dax]; - if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); - iscale = swal[dax] * yrepeat; - vince[z] = xs_ToFixed(fracbits, iscale); - vplce[z] = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[z] - CenterY + 0.5)); - } - if (bad == 15) - { - light += rw_lightstep * 4; - continue; - } - - if (!fixed) - { - for (z = 0; z < 4; ++z) - { - light += rw_lightstep; - palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - } - - u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); - d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); - - if ((bad != 0) || (u4 >= d4)) - { - for (z = 0; z < 4; ++z) - { - if (!(bad & 1)) - { - mvline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); - } - bad >>= 1; - } - continue; - } - - for (z = 0; z < 4; ++z) - { - if (u4 > y1ve[z]) - { - vplce[z] = mvline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); - } - } - - if (d4 > u4) - { - dc_count = d4-u4; - dc_dest = ylookup[u4]+p; - domvline4(); - } - - BYTE *i = p+ylookup[d4]; - for (z = 0; z < 4; ++z) - { - if (y2ve[z] > d4) - { - mvline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); - } - } - } - for(; x < x2; ++x, ++p) - { - light += rw_lightstep; - y1ve[0] = uwal[x]; - y2ve[0] = dwal[x]; - if (y2ve[0] <= y1ve[0]) continue; - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + p; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 0.5)); - - domvline1(); - } - -//unclock(WallScanCycles); - - NetUpdate (); -} - -inline void preptmvline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) -{ - dc_iscale = vince; - dc_colormap = colormap; - dc_count = count; - dc_texturefrac = vplce; - dc_source = bufplce; - dc_dest = dest; -} - -void transmaskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, - double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - fixed_t (*tmvline1)(); - void (*tmvline4)(); - int x, fracbits; - BYTE *p; - int y1ve[4], y2ve[4], u4, d4, startx, dax, z; - char bad; - float light = rw_light - rw_lightstep; - SDWORD xoffset; - BYTE *basecolormapdata; - double iscale; - - if (rw_pic->UseType == FTexture::TEX_Null) - { - return; - } - - if (!R_GetTransMaskDrawers (&tmvline1, &tmvline4)) - { - // The current translucency is unsupported, so draw with regular maskwallscan instead. - maskwallscan (x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - return; - } - -//extern cycle_t WallScanCycles; -//clock (WallScanCycles); - - rw_pic->GetHeight(); // Make sure texture size is loaded - fracbits = 32 - rw_pic->HeightBits; - if (fracbits == 32) - { // Hack for one pixel tall textures - fracbits = 0; - yrepeat = 0; - dc_texturemid = 0; - } - setuptmvline(fracbits); - xoffset = rw_offset; - basecolormapdata = basecolormap->Maps; - fixed_t centeryfrac = FLOAT2FIXED(CenterY); - - x = startx = x1; - p = x + dc_destorg; - - bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); - if (fixed) - { - palookupoffse[0] = dc_colormap; - palookupoffse[1] = dc_colormap; - palookupoffse[2] = dc_colormap; - palookupoffse[3] = dc_colormap; - } - - for(; (x < x2) && ((size_t)p & 3); ++x, ++p) - { - light += rw_lightstep; - y1ve[0] = uwal[x];//max(uwal[x],umost[x]); - y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); - if (y2ve[0] <= y1ve[0]) continue; - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + p; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 0.5)); - - tmvline1(); - } - - for(; x < x2-3; x += 4, p+= 4) - { - bad = 0; - for (z = 3, dax = x+3; z >= 0; --z, --dax) - { - y1ve[z] = uwal[dax]; - y2ve[z] = dwal[dax]; - if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); - iscale = swal[dax] * yrepeat; - vince[z] = xs_ToFixed(fracbits, iscale); - vplce[z] = xs_ToFixed(fracbits, dc_texturemid + vince[z] * (y1ve[z] - CenterY + 0.5)); - } - if (bad == 15) - { - light += rw_lightstep * 4; - continue; - } - - if (!fixed) - { - for (z = 0; z < 4; ++z) - { - light += rw_lightstep; - palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - } - - u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); - d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); - - if ((bad != 0) || (u4 >= d4)) - { - for (z = 0; z < 4; ++z) - { - if (!(bad & 1)) - { - preptmvline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); - tmvline1(); - } - bad >>= 1; - } - continue; - } - - for (z = 0; z < 4; ++z) - { - if (u4 > y1ve[z]) - { - preptmvline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); - vplce[z] = tmvline1(); - } - } - - if (d4 > u4) - { - dc_count = d4-u4; - dc_dest = ylookup[u4]+p; - tmvline4(); - } - - BYTE *i = p+ylookup[d4]; - for (z = 0; z < 4; ++z) - { - if (y2ve[z] > d4) - { - preptmvline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); - tmvline1(); - } - } - } - for(; x < x2; ++x, ++p) - { - light += rw_lightstep; - y1ve[0] = uwal[x]; - y2ve[0] = dwal[x]; - if (y2ve[0] <= y1ve[0]) continue; - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + p; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 0.5)); - - tmvline1(); - } - -//unclock(WallScanCycles); - - NetUpdate (); -} - // // R_RenderSegLoop // Draws zero, one, or two textures for walls. diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index 912998d9a..603606341 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -95,7 +95,8 @@ void FResourceLump::LumpNameSetup(FString iname) { long slash = iname.LastIndexOf('/'); FString base = (slash >= 0) ? iname.Mid(slash + 1) : iname; - base.Truncate(base.LastIndexOf('.')); + auto dot = base.LastIndexOf('.'); + if (dot >= 0) base.Truncate(dot); uppercopy(Name, base); Name[8] = 0; FullName = iname; diff --git a/src/serializer.cpp b/src/serializer.cpp index dec81e86f..8875b61f9 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -162,7 +162,7 @@ static const char *StringToUnicode(const char *cc, int size = -1) int count = 0; int count1 = 0; out.Clear(); - while (ch = (*c++) & 255) + while ((ch = (*c++) & 255)) { count1++; if (ch >= 128) @@ -180,7 +180,7 @@ static const char *StringToUnicode(const char *cc, int size = -1) out.Last() = 0; c = cc; int i = 0; - while (ch = (*c++) & 255) + while ((ch = (*c++) & 255)) { utf8_encode(ch, &out[i], &count1); i += count1; diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index b785af765..05469cc52 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -328,6 +328,9 @@ protected: #ifndef DYN_FLUIDSYNTH #include #else +#include "i_module.h" +extern FModule FluidSynthModule; + struct fluid_settings_t; struct fluid_synth_t; #endif @@ -355,40 +358,35 @@ protected: #ifdef DYN_FLUIDSYNTH enum { FLUID_FAILED = -1, FLUID_OK = 0 }; - fluid_settings_t *(*new_fluid_settings)(); - fluid_synth_t *(*new_fluid_synth)(fluid_settings_t *); - int (*delete_fluid_synth)(fluid_synth_t *); - void (*delete_fluid_settings)(fluid_settings_t *); - int (*fluid_settings_setnum)(fluid_settings_t *, const char *, double); - int (*fluid_settings_setstr)(fluid_settings_t *, const char *, const char *); - int (*fluid_settings_setint)(fluid_settings_t *, const char *, int); - int (*fluid_settings_getstr)(fluid_settings_t *, const char *, char **); - int (*fluid_settings_getint)(fluid_settings_t *, const char *, int *); - void (*fluid_synth_set_reverb_on)(fluid_synth_t *, int); - void (*fluid_synth_set_chorus_on)(fluid_synth_t *, int); - int (*fluid_synth_set_interp_method)(fluid_synth_t *, int, int); - int (*fluid_synth_set_polyphony)(fluid_synth_t *, int); - int (*fluid_synth_get_polyphony)(fluid_synth_t *); - int (*fluid_synth_get_active_voice_count)(fluid_synth_t *); - double (*fluid_synth_get_cpu_load)(fluid_synth_t *); - int (*fluid_synth_system_reset)(fluid_synth_t *); - int (*fluid_synth_noteon)(fluid_synth_t *, int, int, int); - int (*fluid_synth_noteoff)(fluid_synth_t *, int, int); - int (*fluid_synth_cc)(fluid_synth_t *, int, int, int); - int (*fluid_synth_program_change)(fluid_synth_t *, int, int); - int (*fluid_synth_channel_pressure)(fluid_synth_t *, int, int); - int (*fluid_synth_pitch_bend)(fluid_synth_t *, int, int); - int (*fluid_synth_write_float)(fluid_synth_t *, int, void *, int, int, void *, int, int); - int (*fluid_synth_sfload)(fluid_synth_t *, const char *, int); - void (*fluid_synth_set_reverb)(fluid_synth_t *, double, double, double, double); - void (*fluid_synth_set_chorus)(fluid_synth_t *, int, double, double, double, int); - int (*fluid_synth_sysex)(fluid_synth_t *, const char *, int, char *, int *, int *, int); + static TReqProc new_fluid_settings; + static TReqProc new_fluid_synth; + static TReqProc delete_fluid_synth; + static TReqProc delete_fluid_settings; + static TReqProc fluid_settings_setnum; + static TReqProc fluid_settings_setstr; + static TReqProc fluid_settings_setint; + static TReqProc fluid_settings_getstr; + static TReqProc fluid_settings_getint; + static TReqProc fluid_synth_set_reverb_on; + static TReqProc fluid_synth_set_chorus_on; + static TReqProc fluid_synth_set_interp_method; + static TReqProc fluid_synth_set_polyphony; + static TReqProc fluid_synth_get_polyphony; + static TReqProc fluid_synth_get_active_voice_count; + static TReqProc fluid_synth_get_cpu_load; + static TReqProc fluid_synth_system_reset; + static TReqProc fluid_synth_noteon; + static TReqProc fluid_synth_noteoff; + static TReqProc fluid_synth_cc; + static TReqProc fluid_synth_program_change; + static TReqProc fluid_synth_channel_pressure; + static TReqProc fluid_synth_pitch_bend; + static TReqProc fluid_synth_write_float; + static TReqProc fluid_synth_sfload; + static TReqProc fluid_synth_set_reverb; + static TReqProc fluid_synth_set_chorus; + static TReqProc fluid_synth_sysex; -#ifdef _WIN32 - HMODULE FluidSynthDLL; -#else - void *FluidSynthSO; -#endif bool LoadFluidSynth(); void UnloadFluidSynth(); #endif diff --git a/src/sound/music_fluidsynth_mididevice.cpp b/src/sound/music_fluidsynth_mididevice.cpp index fd0bab645..bbd6536bc 100644 --- a/src/sound/music_fluidsynth_mididevice.cpp +++ b/src/sound/music_fluidsynth_mididevice.cpp @@ -60,9 +60,9 @@ #include #ifdef __APPLE__ -#define FLUIDSYNTHLIB "libfluidsynth.1.dylib" +#define FLUIDSYNTHLIB1 "libfluidsynth.1.dylib" #else // !__APPLE__ -#define FLUIDSYNTHLIB "libfluidsynth.so.1" +#define FLUIDSYNTHLIB1 "libfluidsynth.so.1" #endif // __APPLE__ #endif @@ -644,12 +644,6 @@ FString FluidSynthMIDIDevice::GetStats() #ifdef DYN_FLUIDSYNTH -struct LibFunc -{ - void **FuncPointer; - const char *FuncName; -}; - //========================================================================== // // FluidSynthMIDIDevice :: LoadFluidSynth @@ -658,124 +652,65 @@ struct LibFunc // //========================================================================== +FModuleMaybe FluidSynthModule{"FluidSynth"}; + +#define DYN_FLUID_SYM(x) decltype(FluidSynthMIDIDevice::x) FluidSynthMIDIDevice::x{#x} +DYN_FLUID_SYM(new_fluid_settings); +DYN_FLUID_SYM(new_fluid_synth); +DYN_FLUID_SYM(delete_fluid_synth); +DYN_FLUID_SYM(delete_fluid_settings); +DYN_FLUID_SYM(fluid_settings_setnum); +DYN_FLUID_SYM(fluid_settings_setstr); +DYN_FLUID_SYM(fluid_settings_setint); +DYN_FLUID_SYM(fluid_settings_getstr); +DYN_FLUID_SYM(fluid_settings_getint); +DYN_FLUID_SYM(fluid_synth_set_reverb_on); +DYN_FLUID_SYM(fluid_synth_set_chorus_on); +DYN_FLUID_SYM(fluid_synth_set_interp_method); +DYN_FLUID_SYM(fluid_synth_set_polyphony); +DYN_FLUID_SYM(fluid_synth_get_polyphony); +DYN_FLUID_SYM(fluid_synth_get_active_voice_count); +DYN_FLUID_SYM(fluid_synth_get_cpu_load); +DYN_FLUID_SYM(fluid_synth_system_reset); +DYN_FLUID_SYM(fluid_synth_noteon); +DYN_FLUID_SYM(fluid_synth_noteoff); +DYN_FLUID_SYM(fluid_synth_cc); +DYN_FLUID_SYM(fluid_synth_program_change); +DYN_FLUID_SYM(fluid_synth_channel_pressure); +DYN_FLUID_SYM(fluid_synth_pitch_bend); +DYN_FLUID_SYM(fluid_synth_write_float); +DYN_FLUID_SYM(fluid_synth_sfload); +DYN_FLUID_SYM(fluid_synth_set_reverb); +DYN_FLUID_SYM(fluid_synth_set_chorus); +DYN_FLUID_SYM(fluid_synth_sysex); + bool FluidSynthMIDIDevice::LoadFluidSynth() { - LibFunc imports[] = - { - { (void **)&new_fluid_settings, "new_fluid_settings" }, - { (void **)&new_fluid_synth, "new_fluid_synth" }, - { (void **)&delete_fluid_synth, "delete_fluid_synth" }, - { (void **)&delete_fluid_settings, "delete_fluid_settings" }, - { (void **)&fluid_settings_setnum, "fluid_settings_setnum" }, - { (void **)&fluid_settings_setstr, "fluid_settings_setstr" }, - { (void **)&fluid_settings_setint, "fluid_settings_setint" }, - { (void **)&fluid_settings_getstr, "fluid_settings_getstr" }, - { (void **)&fluid_settings_getint, "fluid_settings_getint" }, - { (void **)&fluid_synth_set_reverb_on, "fluid_synth_set_reverb_on" }, - { (void **)&fluid_synth_set_chorus_on, "fluid_synth_set_chorus_on" }, - { (void **)&fluid_synth_set_interp_method, "fluid_synth_set_interp_method" }, - { (void **)&fluid_synth_set_polyphony, "fluid_synth_set_polyphony" }, - { (void **)&fluid_synth_get_polyphony, "fluid_synth_get_polyphony" }, - { (void **)&fluid_synth_get_active_voice_count, "fluid_synth_get_active_voice_count" }, - { (void **)&fluid_synth_get_cpu_load, "fluid_synth_get_cpu_load" }, - { (void **)&fluid_synth_system_reset, "fluid_synth_system_reset" }, - { (void **)&fluid_synth_noteon, "fluid_synth_noteon" }, - { (void **)&fluid_synth_noteoff, "fluid_synth_noteoff" }, - { (void **)&fluid_synth_cc, "fluid_synth_cc" }, - { (void **)&fluid_synth_program_change, "fluid_synth_program_change" }, - { (void **)&fluid_synth_channel_pressure, "fluid_synth_channel_pressure" }, - { (void **)&fluid_synth_pitch_bend, "fluid_synth_pitch_bend" }, - { (void **)&fluid_synth_write_float, "fluid_synth_write_float" }, - { (void **)&fluid_synth_sfload, "fluid_synth_sfload" }, - { (void **)&fluid_synth_set_reverb, "fluid_synth_set_reverb" }, - { (void **)&fluid_synth_set_chorus, "fluid_synth_set_chorus" }, - { (void **)&fluid_synth_sysex, "fluid_synth_sysex" }, - }; - int fail = 0; - const char *libname; - -#ifdef _WIN32 if (strlen(fluid_lib) > 0) { - FluidSynthDLL = LoadLibrary(libname = fluid_lib); - if (nullptr == FluidSynthDLL) + if(!FluidSynthModule.Load({fluid_lib})) { + const char* libname = fluid_lib; Printf(TEXTCOLOR_RED "Could not load %s\n", libname); } - } - else - { - FluidSynthDLL = nullptr; - } - - if (nullptr == FluidSynthDLL) - { - FluidSynthDLL = LoadLibrary(libname = FLUIDSYNTHLIB1); - if (nullptr == FluidSynthDLL) - { - FluidSynthDLL = LoadLibrary(libname = FLUIDSYNTHLIB2); - if (nullptr == FluidSynthDLL) - { - Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB1 " or " FLUIDSYNTHLIB2 "\n"); - return false; - } - } - } -#else - if (strlen(fluid_lib) > 0) - { - FluidSynthSO = dlopen(libname = fluid_lib, RTLD_LAZY); - if (nullptr == FluidSynthSO) - { - Printf(TEXTCOLOR_RED "Could not load %s: %s\n", libname, dlerror()); - } - } - else - { - FluidSynthSO = nullptr; + else + return true; } - if (nullptr == FluidSynthSO) +#ifdef FLUIDSYNTHLIB2 + if(!FluidSynthModule.Load({FLUIDSYNTHLIB1, FLUIDSYNTHLIB2})) { - FluidSynthSO = dlopen(libname = FLUIDSYNTHLIB, RTLD_LAZY); - if (nullptr == FluidSynthSO) - { - Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB ": %s\n", dlerror()); - return false; - } - } -#endif - - for (size_t i = 0; i < countof(imports); ++i) - { -#ifdef _WIN32 - FARPROC proc = GetProcAddress(FluidSynthDLL, imports[i].FuncName); -#else - void *proc = dlsym(FluidSynthSO, imports[i].FuncName); -#endif - if (proc == NULL) - { - Printf(TEXTCOLOR_RED"Failed to find %s in %s\n", imports[i].FuncName, libname); - fail++; - } - *imports[i].FuncPointer = (void *)proc; - } - if (fail == 0) - { - return true; - } - else - { -#ifdef _WIN32 - FreeLibrary(FluidSynthDLL); - FluidSynthDLL = NULL; -#else - dlclose(FluidSynthSO); - FluidSynthSO = NULL; -#endif + Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB1 " or " FLUIDSYNTHLIB2 "\n"); return false; } - +#else + if(!FluidSynthModule.Load({fluid_lib, FLUIDSYNTHLIB1})) + { + Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB1 ": %s\n", dlerror()); + return false; + } +#endif + return true; } //========================================================================== @@ -786,19 +721,7 @@ bool FluidSynthMIDIDevice::LoadFluidSynth() void FluidSynthMIDIDevice::UnloadFluidSynth() { -#ifdef _WIN32 - if (FluidSynthDLL != NULL) - { - FreeLibrary(FluidSynthDLL); - FluidSynthDLL = NULL; - } -#else - if (FluidSynthSO != NULL) - { - dlclose(FluidSynthSO); - FluidSynthSO = NULL; - } -#endif + FluidSynthModule.Unload(); } #endif diff --git a/src/sound/oalload.h b/src/sound/oalload.h index ca02d25d9..1fcf53773 100644 --- a/src/sound/oalload.h +++ b/src/sound/oalload.h @@ -3,24 +3,9 @@ #if !defined NO_OPENAL && defined DYN_OPENAL -#ifndef _WIN32 -typedef void* FARPROC; -#endif - -#define DEFINE_ENTRY(type, name) static type p_##name; +#define DEFINE_ENTRY(type, name) static TReqProc p_##name{#name}; #include "oaldef.h" #undef DEFINE_ENTRY -struct oalloadentry -{ - const char *name; - FARPROC *funcaddr; -}; -static oalloadentry oalfuncs[] = { -#define DEFINE_ENTRY(type, name) { #name, (FARPROC*)&p_##name }, -#include "oaldef.h" -#undef DEFINE_ENTRY -{ NULL, 0 } -}; #ifndef IN_IDE_PARSER #define alEnable p_alEnable diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 21370d082..c1973780b 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -55,29 +55,25 @@ #include "actor.h" #include "r_state.h" #include "w_wad.h" +#include "i_module.h" #include "i_music.h" #include "i_musicinterns.h" #include "tempfiles.h" +FModule OpenALModule{"OpenAL"}; + #include "oalload.h" CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) #ifdef _WIN32 -static HMODULE hmodOpenAL; #define OPENALLIB "openal32.dll" -#else -static void* hmodOpenAL; -#ifdef __APPLE__ +#elif defined(__APPLE__) #define OPENALLIB "OpenAL.framework/OpenAL" #else #define OPENALLIB "libopenal.so.1" #endif -#define LoadLibrary(x) dlopen((x), RTLD_LAZY) -#define GetProcAddress(a,b) dlsym((a),(b)) -#define FreeLibrary(x) dlclose((x)) -#endif bool IsOpenALPresent() { @@ -92,29 +88,7 @@ bool IsOpenALPresent() if (!done) { done = true; - if (hmodOpenAL == NULL) - { - hmodOpenAL = LoadLibrary(NicePath("$PROGDIR/" OPENALLIB)); - if (hmodOpenAL == NULL) - { - hmodOpenAL = LoadLibrary(OPENALLIB); - if (hmodOpenAL == NULL) - { - return false; - } - } - for(int i = 0; oalfuncs[i].name != NULL; i++) - { - *oalfuncs[i].funcaddr = GetProcAddress(hmodOpenAL, oalfuncs[i].name); - if (*oalfuncs[i].funcaddr == NULL) - { - FreeLibrary(hmodOpenAL); - hmodOpenAL = NULL; - return false; - } - } - } - cached_result = true; + cached_result = OpenALModule.Load({NicePath("$PROGDIR/" OPENALLIB), OPENALLIB}); } return cached_result; #endif diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 9edcf625b..51c5eb12c 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -1277,7 +1277,7 @@ void DCanvas::FinishSimplePolys() void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, - FDynamicColormap *colormap, int lightlevel) + FDynamicColormap *colormap, int lightlevel, int bottomclip) { #ifndef NO_SWRENDER // Use an equation similar to player sprites to determine shade @@ -1295,6 +1295,11 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, return; } + if (bottomclip <= 0) + { + bottomclip = Height; + } + // Find the extents of the polygon, in particular the highest and lowest points. for (botpt = toppt = 0, boty = topy = points[0].Y, leftx = rightx = points[0].X, i = 1; i <= npoints; ++i) { @@ -1317,7 +1322,7 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, rightx = points[i].X; } } - if (topy >= Height || // off the bottom of the screen + if (topy >= bottomclip || // off the bottom of the screen boty <= 0 || // off the top of the screen leftx >= Width || // off the right of the screen rightx <= 0) // off the left of the screen @@ -1343,10 +1348,26 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, R_SetupSpanBits(tex); R_SetSpanColormap(colormap != NULL ? &colormap->Maps[clamp(shade >> FRACBITS, 0, NUMCOLORMAPS-1) * 256] : identitymap); R_SetSpanSource(tex->GetPixels()); - scalex = double(1u << (32 - ds_xbits)) / scalex; - scaley = double(1u << (32 - ds_ybits)) / scaley; - ds_xstep = xs_RoundToInt(cosrot * scalex); - ds_ystep = xs_RoundToInt(sinrot * scaley); + if (ds_xbits != 0) + { + scalex = double(1u << (32 - ds_xbits)) / scalex; + ds_xstep = xs_RoundToInt(cosrot * scalex); + } + else + { // Texture is one pixel wide. + scalex = 0; + ds_xstep = 0; + } + if (ds_ybits != 0) + { + scaley = double(1u << (32 - ds_ybits)) / scaley; + ds_ystep = xs_RoundToInt(sinrot * scaley); + } + else + { // Texture is one pixel tall. + scaley = 0; + ds_ystep = 0; + } // Travel down the right edge and create an outline of that edge. pt1 = toppt; @@ -1356,13 +1377,13 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, { x = FLOAT2FIXED(points[pt1].X + 0.5f); y2 = xs_RoundToInt(points[pt2].Y + 0.5f); - if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= Height && y2 >= Height)) + if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) { } else { fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); - int y3 = MIN(y2, Height); + int y3 = MIN(y2, bottomclip); if (y1 < 0) { x += xinc * -y1; @@ -1387,13 +1408,13 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, { x = FLOAT2FIXED(points[pt1].X + 0.5f); y2 = xs_RoundToInt(points[pt2].Y + 0.5f); - if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= Height && y2 >= Height)) + if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) { } else { fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); - int y3 = MIN(y2, Height); + int y3 = MIN(y2, bottomclip); if (y1 < 0) { x += xinc * -y1; diff --git a/src/v_video.cpp b/src/v_video.cpp index 86041334a..efdfa3971 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -878,8 +878,6 @@ void DFrameBuffer::DrawRateStuff () int rate_x; int textScale = active_con_scale(); - if (textScale == 0) - textScale = CleanXfac; chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2u ms (%3u fps)", howlong, LastCount); rate_x = Width / textScale - ConFont->StringWidth(&fpsbuff[0]); diff --git a/src/v_video.h b/src/v_video.h index d19a3b06e..890ab6d63 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -224,7 +224,7 @@ public: // Fill a simple polygon with a texture virtual void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, - struct FDynamicColormap *colormap, int lightlevel); + struct FDynamicColormap *colormap, int lightlevel, int bottomclip); // Set an area to a specified color virtual void Clear (int left, int top, int right, int bottom, int palcolor, uint32 color); diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 52e78c3bc..db92467bc 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -68,6 +68,7 @@ #include "doomtype.h" #include "m_argv.h" #include "d_main.h" +#include "i_module.h" #include "i_system.h" #include "c_console.h" #include "version.h" @@ -84,6 +85,8 @@ #include "stats.h" #include "st_start.h" +#include "optwin32.h" + #include // MACROS ------------------------------------------------------------------ @@ -143,6 +146,21 @@ LONG GameTitleFontHeight; LONG DefaultGUIFontHeight; LONG ErrorIconChar; +FModule Kernel32Module{"Kernel32"}; +FModule Shell32Module{"Shell32"}; +FModule User32Module{"User32"}; + +namespace OptWin32 { +#define DYN_WIN32_SYM(x) decltype(x) x{#x} + +DYN_WIN32_SYM(SHGetFolderPathA); +DYN_WIN32_SYM(SHGetKnownFolderPath); +DYN_WIN32_SYM(GetLongPathNameA); +DYN_WIN32_SYM(GetMonitorInfoA); + +#undef DYN_WIN32_SYM +} // namespace OptWin32 + // PRIVATE DATA DEFINITIONS ------------------------------------------------ static const char WinClassName[] = GAMENAME "MainWindow"; @@ -818,6 +836,11 @@ void DoMain (HINSTANCE hInstance) Args = new DArgs(__argc, __argv); + // Load Win32 modules + Kernel32Module.Load({"kernel32.dll"}); + Shell32Module.Load({"shell32.dll"}); + User32Module.Load({"user32.dll"}); + // Under XP, get our session ID so we can know when the user changes/locks sessions. // Since we need to remain binary compatible with older versions of Windows, we // need to extract the ProcessIdToSessionId function from kernel32.dll manually. diff --git a/src/win32/i_specialpaths.cpp b/src/win32/i_specialpaths.cpp index ed8dc2ee6..6d4890ea9 100644 --- a/src/win32/i_specialpaths.cpp +++ b/src/win32/i_specialpaths.cpp @@ -43,7 +43,7 @@ #include "version.h" // for GAMENAME #include "i_system.h" -typedef HRESULT (WINAPI *GKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *); +#include "optwin32.h" //=========================================================================== // @@ -94,19 +94,17 @@ bool UseKnownFolders() bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create, FString &path) { - static TOptWin32Proc SHGetKnownFolderPath("shell32.dll", "SHGetKnownFolderPath"); + using OptWin32::SHGetFolderPathA; + using OptWin32::SHGetKnownFolderPath; char pathstr[MAX_PATH]; // SHGetKnownFolderPath knows about more folders than SHGetFolderPath, but is // new to Vista, hence the reason we support both. - if (SHGetKnownFolderPath == NULL) + if (!SHGetKnownFolderPath) { - static TOptWin32Proc - SHGetFolderPathA("shell32.dll", "SHGetFolderPathA"); - // NT4 doesn't even have this function. - if (SHGetFolderPathA == NULL) + if (!SHGetFolderPathA) return false; if (shell_folder < 0) @@ -117,7 +115,7 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create { shell_folder |= CSIDL_FLAG_CREATE; } - if (FAILED(SHGetFolderPathA.Call(NULL, shell_folder, NULL, 0, pathstr))) + if (FAILED(SHGetFolderPathA(NULL, shell_folder, NULL, 0, pathstr))) { return false; } @@ -127,7 +125,7 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create else { PWSTR wpath; - if (FAILED(SHGetKnownFolderPath.Call(known_folder, create ? KF_FLAG_CREATE : 0, NULL, &wpath))) + if (FAILED(SHGetKnownFolderPath(known_folder, create ? KF_FLAG_CREATE : 0, NULL, &wpath))) { return false; } diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index f7cd1b9bf..aae738d74 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -86,6 +86,8 @@ #include "textures/bitmap.h" #include "textures/textures.h" +#include "optwin32.h" + // MACROS ------------------------------------------------------------------ #ifdef _MSC_VER @@ -1716,20 +1718,19 @@ unsigned int I_MakeRNGSeed() FString I_GetLongPathName(FString shortpath) { - static TOptWin32Proc - GetLongPathNameA("kernel32.dll", "GetLongPathNameA"); + using OptWin32::GetLongPathNameA; // Doesn't exist on NT4 - if (GetLongPathName == NULL) + if (!GetLongPathName) return shortpath; - DWORD buffsize = GetLongPathNameA.Call(shortpath.GetChars(), NULL, 0); + DWORD buffsize = GetLongPathNameA(shortpath.GetChars(), NULL, 0); if (buffsize == 0) { // nothing to change (it doesn't exist, maybe?) return shortpath; } TCHAR *buff = new TCHAR[buffsize]; - DWORD buffsize2 = GetLongPathNameA.Call(shortpath.GetChars(), buff, buffsize); + DWORD buffsize2 = GetLongPathNameA(shortpath.GetChars(), buff, buffsize); if (buffsize2 >= buffsize) { // Failure! Just return the short path delete[] buff; diff --git a/src/win32/i_system.h b/src/win32/i_system.h index dde901472..cb5894c52 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -52,30 +52,6 @@ typedef enum { extern os_t OSPlatform; -// Helper template so that we can access newer Win32 functions with a single static -// variable declaration. If this were C++11 it could be totally transparent. -template -class TOptWin32Proc -{ - static Proto GetOptionalWin32Proc(const char* module, const char* function) - { - HMODULE hmodule = GetModuleHandle(module); - if (hmodule == NULL) - return NULL; - - return (Proto)GetProcAddress(hmodule, function); - } - -public: - const Proto Call; - - TOptWin32Proc(const char* module, const char* function) - : Call(GetOptionalWin32Proc(module, function)) {} - - // Wrapper object can be tested against NULL, but not directly called. - operator const void*() const { return Call; } -}; - // Called by DoomMain. void I_Init (void); diff --git a/src/win32/optwin32.h b/src/win32/optwin32.h new file mode 100644 index 000000000..0f1458c57 --- /dev/null +++ b/src/win32/optwin32.h @@ -0,0 +1,24 @@ +#pragma once + +// Forward declarations for optional Win32 API procedures +// implemented in i_main.cpp + +#define WIN32_LEAN_AND_MEAN +#include +#include +#define USE_WINDOWS_DWORD + +#include "i_module.h" + +extern FModule Kernel32Module; +extern FModule Shell32Module; +extern FModule User32Module; + +namespace OptWin32 { + +extern TOptProc SHGetFolderPathA; +extern TOptProc SHGetKnownFolderPath; +extern TOptProc GetLongPathNameA; +extern TOptProc GetMonitorInfoA; + +} // namespace OptWin32 diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index 591e17a46..653ad303a 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -264,7 +264,7 @@ public: void DrawPixel(int x, int y, int palcolor, uint32 rgbcolor); void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, - DAngle rotation, FDynamicColormap *colormap, int lightlevel); + DAngle rotation, FDynamicColormap *colormap, int lightlevel, int bottomclip) override; bool WipeStartScreen(int type); void WipeEndScreen(); bool WipeDo(int ticks); diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index 178427862..a260c44b7 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -73,6 +73,8 @@ #include "win32iface.h" +#include "optwin32.h" + // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- diff --git a/src/zstring.cpp b/src/zstring.cpp index f4a3a1fbb..e8e867323 100644 --- a/src/zstring.cpp +++ b/src/zstring.cpp @@ -343,28 +343,74 @@ FString &FString::operator += (char tail) FString &FString::AppendCStrPart (const char *tail, size_t tailLen) { - size_t len1 = Len(); - ReallocBuffer (len1 + tailLen); - StrCopy (Chars + len1, tail, tailLen); + if (tailLen > 0) + { + size_t len1 = Len(); + ReallocBuffer(len1 + tailLen); + StrCopy(Chars + len1, tail, tailLen); + } return *this; } FString &FString::CopyCStrPart(const char *tail, size_t tailLen) { - ReallocBuffer(tailLen); - StrCopy(Chars, tail, tailLen); + if (tailLen > 0) + { + ReallocBuffer(tailLen); + StrCopy(Chars, tail, tailLen); + } + else + { + Data()->Release(); + NullString.RefCount++; + Chars = &NullString.Nothing[0]; + } return *this; } void FString::Truncate(long newlen) { - if (newlen >= 0 && newlen < (long)Len()) + if (newlen <= 0) + { + Data()->Release(); + NullString.RefCount++; + Chars = &NullString.Nothing[0]; + } + else if (newlen < (long)Len()) { ReallocBuffer (newlen); Chars[newlen] = '\0'; } } +void FString::Remove(size_t index, size_t remlen) +{ + if (index < Len()) + { + if (index + remlen >= Len()) + { + Truncate((long)index); + } + else + { + remlen = Len() - remlen < remlen ? Len() - remlen : remlen; + if (Data()->RefCount == 1) + { // Can do this in place + memmove(Chars + index, Chars + index + remlen, Len() - index - remlen); + Data()->Len -= (unsigned)remlen; + } + else + { // Must do it in a copy + FStringData *old = Data(); + AllocBuffer(old->Len - remlen); + StrCopy(Chars, old->Chars(), index); + StrCopy(Chars + index, old->Chars() + index + remlen, old->Len - index - remlen); + old->Release(); + } + } + } +} + FString FString::Left (size_t numChars) const { size_t len = Len(); @@ -586,9 +632,13 @@ void FString::StripLeft () if (max == 0) return; for (i = 0; i < max; ++i) { - if (!isspace(Chars[i])) + if (!isspace((unsigned char)Chars[i])) break; } + if (i == 0) + { // Nothing to strip. + return; + } if (Data()->RefCount <= 1) { for (j = 0; i <= max; ++j, ++i) @@ -620,6 +670,10 @@ void FString::StripLeft (const char *charset) if (!strchr (charset, Chars[i])) break; } + if (i == 0) + { // Nothing to strip. + return; + } if (Data()->RefCount <= 1) { for (j = 0; i <= max; ++j, ++i) @@ -640,11 +694,16 @@ void FString::StripLeft (const char *charset) void FString::StripRight () { size_t max = Len(), i; - for (i = max; i-- > 0; ) + if (max == 0) return; + for (i = --max; i-- > 0; ) { - if (!isspace(Chars[i])) + if (!isspace((unsigned char)Chars[i])) break; } + if (i == max) + { // Nothing to strip. + return; + } if (Data()->RefCount <= 1) { Chars[i+1] = '\0'; @@ -668,11 +727,15 @@ void FString::StripRight (const char *charset) { size_t max = Len(), i; if (max == 0) return; - for (i = max; i-- > 0; ) + for (i = --max; i-- > 0; ) { if (!strchr (charset, Chars[i])) break; } + if (i == max) + { // Nothing to strip. + return; + } if (Data()->RefCount <= 1) { Chars[i+1] = '\0'; @@ -693,14 +756,18 @@ void FString::StripLeftRight () if (max == 0) return; for (i = 0; i < max; ++i) { - if (!isspace(Chars[i])) + if (!isspace((unsigned char)Chars[i])) break; } for (j = max - 1; j >= i; --j) { - if (!isspace(Chars[j])) + if (!isspace((unsigned char)Chars[j])) break; } + if (i == 0 && j == max - 1) + { // Nothing to strip. + return; + } if (Data()->RefCount <= 1) { for (k = 0; i <= j; ++i, ++k) @@ -713,8 +780,8 @@ void FString::StripLeftRight () else { FStringData *old = Data(); - AllocBuffer (j - i); - StrCopy (Chars, old->Chars(), j - i); + AllocBuffer(j - i + 1); + StrCopy(Chars, old->Chars(), j - i + 1); old->Release(); } } @@ -768,25 +835,28 @@ void FString::Insert (size_t index, const char *instr) void FString::Insert (size_t index, const char *instr, size_t instrlen) { - size_t mylen = Len(); - if (index > mylen) + if (instrlen > 0) { - index = mylen; - } - if (Data()->RefCount <= 1) - { - ReallocBuffer (mylen + instrlen); - memmove (Chars + index + instrlen, Chars + index, (mylen - index + 1)*sizeof(char)); - memcpy (Chars + index, instr, instrlen*sizeof(char)); - } - else - { - FStringData *old = Data(); - AllocBuffer (mylen + instrlen); - StrCopy (Chars, old->Chars(), index); - StrCopy (Chars + index, instr, instrlen); - StrCopy (Chars + index + instrlen, old->Chars() + index, mylen - index); - old->Release(); + size_t mylen = Len(); + if (index >= mylen) + { + AppendCStrPart(instr, instrlen); + } + else if (Data()->RefCount <= 1) + { + ReallocBuffer(mylen + instrlen); + memmove(Chars + index + instrlen, Chars + index, (mylen - index + 1) * sizeof(char)); + memcpy(Chars + index, instr, instrlen * sizeof(char)); + } + else + { + FStringData *old = Data(); + AllocBuffer(mylen + instrlen); + StrCopy(Chars, old->Chars(), index); + StrCopy(Chars + index, instr, instrlen); + StrCopy(Chars + index + instrlen, old->Chars() + index, mylen - index); + old->Release(); + } } } @@ -970,7 +1040,7 @@ octdigits = [0-7]; yych = *YYCURSOR; // Skip preceding whitespace - while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; } + while (yych != '\0' && isspace((unsigned char)yych)) { yych = *++YYCURSOR; } // Check for sign if (yych == '+' || yych == '-') { yych = *++YYCURSOR; } @@ -1008,7 +1078,7 @@ octdigits = [0-7]; } // The rest should all be whitespace - while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; } + while (yych != '\0' && isspace((unsigned char)yych)) { yych = *++YYCURSOR; } return yych == '\0'; } @@ -1028,7 +1098,7 @@ digits = [0-9]; yych = *YYCURSOR; // Skip preceding whitespace - while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; } + while (yych != '\0' && isspace((unsigned char)yych)) { yych = *++YYCURSOR; } // Check for sign if (yych == '+' || yych == '-') { yych = *++YYCURSOR; } @@ -1059,7 +1129,7 @@ digits = [0-9]; } // The rest should all be whitespace - while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; } + while (yych != '\0' && isspace((unsigned char)yych)) { yych = *++YYCURSOR; } return yych == '\0'; } diff --git a/src/zstring.h b/src/zstring.h index 00f373b59..ba9208719 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -268,6 +268,7 @@ public: bool IsNotEmpty() const { return Len() != 0; } void Truncate (long newlen); + void Remove(size_t index, size_t remlen); int Compare (const FString &other) const { return strcmp (Chars, other.Chars); } int Compare (const char *other) const { return strcmp (Chars, other); } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 5c1961ea4..448506727 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1784,6 +1784,7 @@ DSPLYMNU_WIPETYPE = "Screen wipe style"; DSPLYMNU_SHOWENDOOM = "Show ENDOOM screen"; DSPLYMNU_BLOODFADE = "Blood Flash Intensity"; DSPLYMNU_PICKUPFADE = "Pickup Flash Intensity"; +DSPLYMNU_WATERFADE = "Underwater Blend Intensity"; DSPLYMNU_PALLETEHACK = "DirectDraw palette hack"; // Not used DSPLYMNU_ATTACHEDSURFACES = "Use attached surfaces"; // Not used DSPLYMNU_SKYMODE = "Sky render mode"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index cc1fca117..98f53c9be 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -683,6 +683,7 @@ OptionMenu "VideoOptions" Option "$DSPLYMNU_CAPFPS", "cl_capfps", "OffOn" Slider "$DSPLYMNU_BLOODFADE", "blood_fade_scalar", 0.0, 1.0, 0.05, 2 Slider "$DSPLYMNU_PICKUPFADE", "pickup_fade_scalar", 0.0, 1.0, 0.05, 2 + Slider "$DSPLYMNU_WATERFADE", "underwater_fade_scalar", 0.0, 1.0, 0.05, 2 Option "$DSPLYMNU_COLUMNMETHOD", "r_columnmethod", "ColumnMethods" StaticText " "