diff --git a/.gitignore b/.gitignore index 41bc0ea83..af8db2df5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ scripts/work/ /SDL.dll /freetype.dll /autoexec.cfg +other/freetype +other/sdl _test.exe Info.plist diff --git a/bam.lua b/bam.lua index 1988d217c..09dd365f2 100644 --- a/bam.lua +++ b/bam.lua @@ -268,6 +268,12 @@ function GenerateWindowsSettings(settings, conf, target_arch, compiler) -- Content BuildContent(settings) + + -- dependencies + AddJob("other/sdl/include/SDL.h", "Downloading SDL2 headers and DLL...", dl .. " sdl SDL2.dll") -- TODO: split up dll and headers! + AddJob("other/freetype/include/ft2build.h", "Downloading freetype headers and DLL...", dl .. " freetype freetype.dll") + AddDependency(cur_builddir .. "/objs/engine/client/backend_sdl" .. settings.cc.extension, "other/sdl/include/SDL.h") + AddDependency(cur_builddir .. "/objs/engine/client/text" .. settings.cc.extension, "other/freetype/include/ft2build.h") end function SharedCommonFiles() @@ -433,7 +439,7 @@ function GenerateSettings(conf, arch, builddir, compiler) return settings end --- String formatting wth named parameters, by RiciLake http://lua-users.org/wiki/StringInterpolation +-- String formatting with named parameters, by RiciLake http://lua-users.org/wiki/StringInterpolation function interp(s, tab) return (s:gsub('%%%((%a%w*)%)([-0-9%.]*[cdeEfgGiouxXsq])', function(k, fmt) @@ -495,12 +501,19 @@ end for a, cur_arch in ipairs(archs) do for c, cur_conf in ipairs(confs) do cur_builddir = interp(builddir, {platform=family, arch=cur_arch, target=cur_target, conf=cur_conf, compiler=compiler}) + if family == "windows" then + dl = Python("scripts/download.py") + dl = dl .. " --arch " .. cur_arch .. " --conf " .. cur_conf + AddJob(cur_builddir .. "/SDL2.dll", "Downloading SDL.dll for " .. cur_arch .. "/" .. cur_conf, dl .. " SDL2.dll") -- TODO: Make me working! + AddJob(cur_builddir .. "/freetype.dll", "Downloading freetype.dll for " .. cur_arch .. "/" .. cur_conf, dl .. " freetype.dll") + end local settings = GenerateSettings(cur_conf, cur_arch, cur_builddir, compiler) for t, cur_target in pairs(targets) do table.insert(subtargets[cur_target], PathJoin(cur_builddir, cur_target .. settings.link.extension)) end end end + for cur_name, cur_target in pairs(targets) do -- Supertarget for all configurations and architectures of that target PseudoTarget(cur_name, subtargets[cur_target]) diff --git a/scripts/download.py b/scripts/download.py new file mode 100644 index 000000000..850af96e7 --- /dev/null +++ b/scripts/download.py @@ -0,0 +1,61 @@ +import shutil, os, re, sys, zipfile +from distutils.dir_util import copy_tree +os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])) + "/..") +import twlib + +def unzip(filename, where): + try: + z = zipfile.ZipFile(filename, "r") + except: + return False + for name in z.namelist(): + z.extract(name, where) + z.close() + return z.namelist()[0] + +def downloadAll(arch, conf, targets): + url = "https://github.com/teeworlds/teeworlds-libs/archive/master.zip" + if arch == "x86_64": + _arch = "x64" + else: + _arch = arch + builddir = "build/" + arch + "/" + conf + "/" + + # download and unzip + src_package_libs = twlib.fetch_file(url) + if not src_package_libs: + print("couldn't download libs") + sys.exit(-1) + libs_dir = unzip(src_package_libs, ".") + if not libs_dir: + print("couldn't unzip libs") + sys.exit(-1) + libs_dir = "teeworlds-libs-master" + + if "SDL2.dll" in targets: + shutil.copy(libs_dir + "/sdl/windows/lib/" + _arch + "/SDL2.dll", builddir) + if "freetype.dll" in targets: + shutil.copy(libs_dir + "/freetype/windows/lib/" + _arch + "/freetype.dll", builddir) + if "sdl" in targets: + copy_tree(libs_dir + "/sdl/windows/", "other/sdl/") + if "freetype" in targets: + copy_tree(libs_dir + "/freetype/windows/", "other/freetype/") + + # cleanup + try: + shutil.rmtree(libs_dir) + os.remove(src_package_libs) + except: pass + +def main(): + import argparse + p = argparse.ArgumentParser(description="Download freetype and SDL library and header files for Windows.") + p.add_argument("--arch", default="x86", choices=["x86", "x86_64"], help="Architecture for the downloaded libraries (Default: x86)") + p.add_argument("--conf", default="debug", choices=["debug", "release"], help="Build type (Default: debug)") + p.add_argument("targets", metavar="TARGET", nargs='+', choices=["SDL2.dll", "freetype.dll", "sdl", "freetype"], help='Target to download. Valid choices are "SDL.dll", "freetype.dll", "sdl" and "freetype"') + args = p.parse_args() + + downloadAll(args.arch, args.conf, args.targets) + +if __name__ == '__main__': + main() diff --git a/src/base/system.c b/src/base/system.c index 92e090622..098e48c3d 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -1798,6 +1798,22 @@ void str_sanitize(char *str_in) } } +/* removes all forbidden windows/unix characters in filenames*/ +char* str_sanitize_filename(char* aName) +{ + char *str = (char *)aName; + while(*str) + { + // replace forbidden characters with a whispace + if(*str == '/' || *str == '<' || *str == '>' || *str == ':' || *str == '"' + || *str == '/' || *str == '\\' || *str == '|' || *str == '?' || *str == '*') + *str = ' '; + str++; + } + str_clean_whitespaces(aName); + return aName; +} + /* removes leading and trailing spaces and limits the use of multiple spaces */ void str_clean_whitespaces(char *str_in) { diff --git a/src/base/system.h b/src/base/system.h index 318b4f3d9..b2a74b024 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -841,6 +841,19 @@ void str_sanitize_cc(char *str); */ void str_sanitize(char *str); +/* + Function: str_sanitize_filename + Replaces all forbidden Windows/Unix characters with whitespace + or nothing if leading or trailing. + + Parameters: + str - String to sanitize. + + Remarks: + - The strings are treated as zero-terminated strings. +*/ +char* str_sanitize_filename(char* aName); + /* Function: str_check_pathname Check if the string contains '..' (parent directory) paths. diff --git a/src/engine/client/backend_sdl.cpp b/src/engine/client/backend_sdl.cpp index 81de48b0b..f22bfff01 100644 --- a/src/engine/client/backend_sdl.cpp +++ b/src/engine/client/backend_sdl.cpp @@ -12,11 +12,10 @@ #include "graphics_threaded.h" #include "backend_sdl.h" - #if defined(CONF_FAMILY_WINDOWS) PFNGLTEXIMAGE3DPROC glTexImage3DInternal; - void GLAPIENTRY glTexImage3D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +GLAPI void APIENTRY glTexImage3D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) { glTexImage3DInternal(target, level, internalFormat, width, height, depth, border, format, type, pixels); } @@ -160,7 +159,7 @@ void CCommandProcessorFragment_OpenGL::SetState(const CCommandBuffer::SState &St } else glDisable(GL_SCISSOR_TEST); - + // texture int SrcBlendMode = GL_ONE; @@ -335,7 +334,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer:: } } m_aTextures[pCommand->m_Slot].m_Format = pCommand->m_Format; - + // int Oglformat = TexFormatToOpenGLFormat(pCommand->m_Format); int StoreOglformat = TexFormatToOpenGLFormat(pCommand->m_StoreFormat); @@ -384,7 +383,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer:: m_aTextures[pCommand->m_Slot].m_MemSize += TexWidth*TexHeight*pCommand->m_PixelSize; } } - + // 3D texture if((pCommand->m_Flags&CCommandBuffer::TEXFLAG_TEXTURE3D) && m_Max3DTexSize >= CTexture::MIN_GL_MAX_3D_TEXTURE_SIZE) { @@ -410,12 +409,12 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer:: } mem_free(pTexData); - + // glGenTextures(m_TextureArraySize, m_aTextures[pCommand->m_Slot].m_Tex3D); m_aTextures[pCommand->m_Slot].m_State |= CTexture::STATE_TEX3D; for(int i = 0; i < m_TextureArraySize; ++i) - { + { glBindTexture(GL_TEXTURE_3D, m_aTextures[pCommand->m_Slot].m_Tex3D[i]); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -425,7 +424,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer:: m_aTextures[pCommand->m_Slot].m_MemSize += Width*Height*pCommand->m_PixelSize; } pTexData = pTmpData; - } + } *m_pTextureMemoryUsage += m_aTextures[pCommand->m_Slot].m_MemSize; @@ -441,7 +440,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_ void CCommandProcessorFragment_OpenGL::Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand) { SetState(pCommand->m_State); - + glVertexPointer(3, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices); glTexCoordPointer(3, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices + sizeof(float)*3); glColorPointer(4, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices + sizeof(float)*6); @@ -472,7 +471,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Screenshot(const CCommandBuffer::SCom int h = pCommand->m_H == -1 ? aViewport[3] : pCommand->m_H; int x = pCommand->m_X; int y = aViewport[3] - pCommand->m_Y - 1 - (h - 1); - + // we allocate one more row to use when we are flipping the texture unsigned char *pPixelData = (unsigned char *)mem_alloc(w*(h+1)*3, 1); unsigned char *pTempRow = pPixelData+w*h*3; @@ -626,16 +625,16 @@ void CCommandProcessor_SDL_OpenGL::RunBuffer(CCommandBuffer *pBuffer) const CCommandBuffer::SCommand *pBaseCommand = pBuffer->GetCommand(&CmdIndex); if(pBaseCommand == 0x0) break; - + if(m_OpenGL.RunCommand(pBaseCommand)) continue; - + if(m_SDL.RunCommand(pBaseCommand)) continue; if(m_General.RunCommand(pBaseCommand)) continue; - + dbg_msg("graphics", "unknown command %d", pBaseCommand->m_Cmd); } } @@ -703,7 +702,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt #else SdlFlags |= SDL_WINDOW_FULLSCREEN; #endif - + // set gl attributes SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); if(FsaaSamples) @@ -786,7 +785,7 @@ int CGraphicsBackend_SDL_OpenGL::Shutdown() CmdBuffer.AddCommand(Cmd); RunBuffer(&CmdBuffer); WaitForIdle(); - + // stop and delete the processor StopProcessor(); delete m_pProcessor; diff --git a/src/engine/client/sound.cpp b/src/engine/client/sound.cpp index 0c627640b..1bb5eed7e 100644 --- a/src/engine/client/sound.cpp +++ b/src/engine/client/sound.cpp @@ -125,21 +125,23 @@ static void Mix(short *pFinalOut, unsigned Frames) float Dist = sqrtf((float)dx*dx+dy*dy); if(Dist >= 0.0f && Dist < m_MaxDistance) { - // constant panning (-3dB center) - float a = 0.5f; - if(dx < 0) - a -= (Dist/m_MaxDistance)/2.0f; - else - a += (Dist/m_MaxDistance)/2.0f; - - float Lgain = sinf((1-a)*pi/2.0f); - float Rgain = sinf(a*pi/2.0f); - // linear falloff float Falloff = 1.0f - Dist/m_MaxDistance; - Lvol = Lvol*Lgain*Falloff; - Rvol = Rvol*Rgain*Falloff; + // amplitude after falloff + float FalloffAmp = v->m_pChannel->m_Vol * Falloff; + + // distribute volume to the channels depending on x difference + float Lpan = 0.5f - dx/m_MaxDistance/2.0f; + float Rpan = 1.0f - Lpan; + + // apply square root to preserve sound power after panning + float LampFactor = sqrt(Lpan); + float RampFactor = sqrt(Rpan); + + // volume of the channels + Lvol = FalloffAmp*LampFactor; + Rvol = FalloffAmp*RampFactor; } else { diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index cce7789c0..d86a8300c 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -760,15 +760,16 @@ void CServer::SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int Cli void CServer::UpdateClientRconCommands() { - int ClientID = Tick() % MAX_CLIENTS; - - if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY && m_aClients[ClientID].m_Authed) + for(int ClientID = Tick() % MAX_RCONCMD_RATIO; ClientID < MaxClients(); ClientID += MAX_RCONCMD_RATIO) { - int ConsoleAccessLevel = m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD; - for(int i = 0; i < MAX_RCONCMD_SEND && m_aClients[ClientID].m_pRconCmdToSend; ++i) + if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY && m_aClients[ClientID].m_Authed) { - SendRconCmdAdd(m_aClients[ClientID].m_pRconCmdToSend, ClientID); - m_aClients[ClientID].m_pRconCmdToSend = m_aClients[ClientID].m_pRconCmdToSend->NextCommandInfo(ConsoleAccessLevel, CFGFLAG_SERVER); + int ConsoleAccessLevel = m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD; + for(int i = 0; i < MAX_RCONCMD_SEND && m_aClients[ClientID].m_pRconCmdToSend; ++i) + { + SendRconCmdAdd(m_aClients[ClientID].m_pRconCmdToSend, ClientID); + m_aClients[ClientID].m_pRconCmdToSend = m_aClients[ClientID].m_pRconCmdToSend->NextCommandInfo(ConsoleAccessLevel, CFGFLAG_SERVER); + } } } } diff --git a/src/engine/server/server.h b/src/engine/server/server.h index f71b943de..2112092f1 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -76,6 +76,7 @@ public: AUTHED_ADMIN, MAX_RCONCMD_SEND=16, + MAX_RCONCMD_RATIO=8, }; class CClient diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index c67708aa4..f1354a290 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -139,6 +139,11 @@ void CChat::ConShowChat(IConsole::IResult *pResult, void *pUserData) ((CChat *)pUserData)->m_Show = pResult->GetInteger(0) != 0; } +void CChat::OnInit() +{ + m_Input.Init(Input()); +} + void CChat::OnConsoleInit() { Console()->Register("say", "r", CFGFLAG_CLIENT, ConSay, this, "Say in chat"); diff --git a/src/game/client/components/chat.h b/src/game/client/components/chat.h index aac6a558c..732a7b79a 100644 --- a/src/game/client/components/chat.h +++ b/src/game/client/components/chat.h @@ -81,6 +81,7 @@ public: void Say(int Team, const char *pLine); + virtual void OnInit(); virtual void OnReset(); virtual void OnConsoleInit(); virtual void OnStateChange(int NewState, int OldState); diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index d275f09cf..bec0c0c9c 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -57,6 +57,7 @@ CGameConsole::CInstance::CInstance(int Type) void CGameConsole::CInstance::Init(CGameConsole *pGameConsole) { m_pGameConsole = pGameConsole; + m_Input.Init(m_pGameConsole->Input()); }; void CGameConsole::CInstance::ClearBacklog() diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 0e836779c..724d9c850 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -448,11 +448,10 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS s_DoScroll = true; s_ScrollStart = UI()->MouseX(); int MxRel = (int)(UI()->MouseX() - pRect->x); - float Offset = pRect->w/2.0f-TextRender()->TextWidth(0, FontSize, pStr, -1)/2.0f; for(int i = 1; i <= Len; i++) { - if(Offset + TextRender()->TextWidth(0, FontSize, pStr, i) - *pOffset > MxRel) + if(TextRender()->TextWidth(0, FontSize, pStr, i) - *pOffset > MxRel) { s_AtIndex = i - 1; break; @@ -487,7 +486,7 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS { Len = str_length(pStr); int NumChars = Len; - ReturnValue |= CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, StrSize, &Len, &s_AtIndex, &NumChars); + ReturnValue |= CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, StrSize, &Len, &s_AtIndex, &NumChars, Input()); } } } @@ -562,15 +561,14 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS UI()->ClipEnable(pRect); Textbox.x -= *pOffset; - UI()->DoLabel(&Textbox, pDisplayStr, FontSize, CUI::ALIGN_CENTER); + UI()->DoLabel(&Textbox, pDisplayStr, FontSize, CUI::ALIGN_LEFT); // render the cursor if(UI()->LastActiveItem() == pID && !JustGotActive) { - float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1); + float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); Textbox = *pRect; - Textbox.x += Textbox.w/2.0f-w/2.0f; - w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); + Textbox.VSplitLeft(2.0f, 0, &Textbox); Textbox.x += (w-*pOffset-TextRender()->TextWidth(0, FontSize, "|", -1)/2); if((2*time_get()/time_freq()) % 2) // make it blink diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index ec9c3526a..7f32e2d24 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -531,7 +531,7 @@ private: void HandleCallvote(int Page, bool Force); void RenderServerControl(CUIRect MainView); void RenderServerControlKick(CUIRect MainView, bool FilterSpectators); - void RenderServerControlServer(CUIRect MainView); + bool RenderServerControlServer(CUIRect MainView); // found in menus_browser.cpp // int m_ScrollOffset; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index ab942cc33..4c37fcef3 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -2132,11 +2132,15 @@ void CMenus::RenderServerbrowserBottomBox(CUIRect MainView) } void CMenus::DoGameIcon(const char *pName, const CUIRect *pRect, int Type) { + char aNameBuf[128]; + str_copy(aNameBuf, pName, sizeof(aNameBuf)); + str_sanitize_filename(aNameBuf); + // get texture IGraphics::CTextureHandle Tex = m_GameIconDefault; for(int i = 0; i < m_lGameIcons.size(); ++i) { - if(!str_comp_nocase(pName, m_lGameIcons[i].m_Name)) + if(!str_comp_nocase(aNameBuf, m_lGameIcons[i].m_Name)) { Tex = m_lGameIcons[i].m_IconTexture; break; diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 00a58e5de..455cc3450 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -469,8 +469,9 @@ void CMenus::RenderServerInfo(CUIRect MainView) TextRender()->Text(0, Motd.x, Motd.y, ButtonHeight*ms_FontmodHeight*0.8f, m_pClient->m_pMotd->GetMotd(), (int)Motd.w); } -void CMenus::RenderServerControlServer(CUIRect MainView) +bool CMenus::RenderServerControlServer(CUIRect MainView) { + bool doCallVote = false; static int s_VoteList = 0; static CListBoxState s_ListBoxState; CUIRect List = MainView; @@ -482,14 +483,15 @@ void CMenus::RenderServerControlServer(CUIRect MainView) CListboxItem Item = UiDoListboxNextItem(&s_ListBoxState, pOption); if(Item.m_Visible) - { + { Item.m_Rect.VMargin(5.0f, &Item.m_Rect); Item.m_Rect.y += 2.0f; UI()->DoLabel(&Item.m_Rect, pOption->m_aDescription, Item.m_Rect.h*ms_FontmodHeight*0.8f, CUI::ALIGN_LEFT); } } - m_CallvoteSelectedOption = UiDoListboxEnd(&s_ListBoxState, 0); + m_CallvoteSelectedOption = UiDoListboxEnd(&s_ListBoxState, &doCallVote); + return doCallVote; } void CMenus::RenderServerControlKick(CUIRect MainView, bool FilterSpectators) @@ -676,9 +678,10 @@ void CMenus::RenderServerControl(CUIRect MainView) MainView.HSplitBottom(90.0f+2*20.0f, &MainView, &Extended); RenderTools()->DrawUIRect(&Extended, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f); + bool doCallVote = false; // render page if(s_ControlPage == 0) - RenderServerControlServer(MainView); + doCallVote = RenderServerControlServer(MainView); // double click triggers vote else if(s_ControlPage == 1) RenderServerControlKick(MainView, false); else if(s_ControlPage == 2) @@ -720,7 +723,7 @@ void CMenus::RenderServerControl(CUIRect MainView) { // call vote static CButtonContainer s_CallVoteButton; - if(DoButton_Menu(&s_CallVoteButton, Localize("Call vote"), 0, &Button)) + if(DoButton_Menu(&s_CallVoteButton, Localize("Call vote"), 0, &Button) || doCallVote) HandleCallvote(s_ControlPage, false); } else diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index b60b0f8a8..50f9f82db 100644 --- a/src/game/client/lineinput.cpp +++ b/src/game/client/lineinput.cpp @@ -1,11 +1,13 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include +#include #include "lineinput.h" CLineInput::CLineInput() { Clear(); + m_pInput = 0; } void CLineInput::Clear() @@ -16,6 +18,11 @@ void CLineInput::Clear() m_NumChars = 0; } +void CLineInput::Init(IInput *pInput) +{ + m_pInput = pInput; +} + void CLineInput::Set(const char *pString) { str_copy(m_Str, pString, sizeof(m_Str)); @@ -30,7 +37,15 @@ void CLineInput::Set(const char *pString) } } -bool CLineInput::Manipulate(IInput::CEvent Event, char *pStr, int StrMaxSize, int StrMaxChars, int *pStrLenPtr, int *pCursorPosPtr, int *pNumCharsPtr) +bool CLineInput::CtrlStop(char c) +{ + // jump to spaces and special ASCII characters + return ((32 <= c && c <= 47) || // !"#$%&'()*+,-./ + (58 <= c && c <= 64) || // :;<=>?@ + (91 <= c && c <= 96)); // [\]^_` +} + +bool CLineInput::Manipulate(IInput::CEvent Event, char *pStr, int StrMaxSize, int StrMaxChars, int *pStrLenPtr, int *pCursorPosPtr, int *pNumCharsPtr, IInput *pInput) { int NumChars = *pNumCharsPtr; int CursorPos = *pCursorPosPtr; @@ -74,31 +89,54 @@ bool CLineInput::Manipulate(IInput::CEvent Event, char *pStr, int StrMaxSize, in if(Event.m_Flags&IInput::FLAG_PRESS) { int Key = Event.m_Key; + bool Ctrl = false; +#ifdef CONF_PLATFORM_MACOSX + if(pInput && (pInput->KeyIsPressed(KEY_LALT) || pInput->KeyIsPressed(KEY_RALT))) +#else + if(pInput && (pInput->KeyIsPressed(KEY_LCTRL) || pInput->KeyIsPressed(KEY_RCTRL))) +#endif + Ctrl = true; if(Key == KEY_BACKSPACE && CursorPos > 0) { - int NewCursorPos = str_utf8_rewind(pStr, CursorPos); + int NewCursorPos = CursorPos; + do + { + NewCursorPos = str_utf8_rewind(pStr, NewCursorPos); + NumChars -= 1; + } while(Ctrl && NewCursorPos > 0 && !CtrlStop(pStr[NewCursorPos - 1])); int CharSize = CursorPos-NewCursorPos; mem_move(pStr+NewCursorPos, pStr+CursorPos, Len - NewCursorPos - CharSize + 1); // +1 == null term CursorPos = NewCursorPos; Len -= CharSize; - if(CharSize > 0) - --NumChars; Changes = true; } else if(Key == KEY_DELETE && CursorPos < Len) { - int p = str_utf8_forward(pStr, CursorPos); - int CharSize = p-CursorPos; + int EndCursorPos = CursorPos; + do + { + EndCursorPos = str_utf8_forward(pStr, EndCursorPos); + NumChars -= 1; + } while(Ctrl && EndCursorPos < Len && !CtrlStop(pStr[EndCursorPos - 1])); + int CharSize = EndCursorPos - CursorPos; mem_move(pStr + CursorPos, pStr + CursorPos + CharSize, Len - CursorPos - CharSize + 1); // +1 == null term Len -= CharSize; - if(CharSize > 0) - --NumChars; Changes = true; } else if(Key == KEY_LEFT && CursorPos > 0) - CursorPos = str_utf8_rewind(pStr, CursorPos); + { + do + { + CursorPos = str_utf8_rewind(pStr, CursorPos); + } while(Ctrl && CursorPos > 0 && !CtrlStop(pStr[CursorPos - 1])); + } else if(Key == KEY_RIGHT && CursorPos < Len) - CursorPos = str_utf8_forward(pStr, CursorPos); + { + do + { + CursorPos = str_utf8_forward(pStr, CursorPos); + } while(Ctrl && CursorPos < Len && !CtrlStop(pStr[CursorPos - 1])); + } else if(Key == KEY_HOME) CursorPos = 0; else if(Key == KEY_END) @@ -114,5 +152,5 @@ bool CLineInput::Manipulate(IInput::CEvent Event, char *pStr, int StrMaxSize, in bool CLineInput::ProcessInput(IInput::CEvent e) { - return Manipulate(e, m_Str, MAX_SIZE, MAX_CHARS, &m_Len, &m_CursorPos, &m_NumChars); + return Manipulate(e, m_Str, MAX_SIZE, MAX_CHARS, &m_Len, &m_CursorPos, &m_NumChars, m_pInput); } diff --git a/src/game/client/lineinput.h b/src/game/client/lineinput.h index 5e1f1d74d..fae3a5ef4 100644 --- a/src/game/client/lineinput.h +++ b/src/game/client/lineinput.h @@ -17,8 +17,10 @@ class CLineInput int m_Len; int m_CursorPos; int m_NumChars; + IInput *m_pInput; public: - static bool Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int StrMaxChars, int *pStrLenPtr, int *pCursorPosPtr, int *pNumCharsPtr); + static bool CtrlStop(char c); + static bool Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int StrMaxChars, int *pStrLenPtr, int *pCursorPosPtr, int *pNumCharsPtr, IInput *pInput); class CCallback { @@ -28,6 +30,7 @@ public: }; CLineInput(); + void Init(IInput *pInput); void Clear(); bool ProcessInput(IInput::CEvent e); void Set(const char *pString); diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 413cfa716..a6c1be23f 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -340,7 +340,7 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str { Len = str_length(pStr); int NumChars = Len; - ReturnValue |= CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, StrSize, &Len, &s_AtIndex, &NumChars); + ReturnValue |= CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, StrSize, &Len, &s_AtIndex, &NumChars, Input()); } }