Moved to SDL 2.0 + IME support

This commit is contained in:
yvt 2013-12-09 17:36:05 +09:00
parent 463e1d2053
commit 87aedb702c
22 changed files with 603 additions and 101 deletions

View File

@ -9,9 +9,9 @@ set(OS_BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_PREFIX_PATH Sources/Externals)
include(FindSDL)
if(NOT SDL_FOUND)
message(FATAL_ERROR "OpenSDL not found, set ENV{SDLDIR} to point to OpenSDL, and optionally set the cmake var SDL_LIBRARY_TEMP to the lib dir")
include(cmake/FindSDL2.cmake)
if(NOT SDL2_FOUND)
message(FATAL_ERROR "SDL 2.0 not found, set ENV{SDL2DIR} to point to SDL 2.0, and optionally set the cmake var SDL2_LIBRARY_TEMP to the lib dir")
endif()
include(FindOpenGL)
@ -152,7 +152,7 @@ endif()
configure_file("${PROJECT_SOURCE_DIR}/OpenSpades.h.in" "${PROJECT_BINARY_DIR}/OpenSpades.h")
configure_file("${PROJECT_SOURCE_DIR}/OpenSpades.rc.in" "${PROJECT_BINARY_DIR}/OpenSpades.rc")
include_directories("${PROJECT_BINARY_DIR}")
include_directories("${SDL_INCLUDE_DIR}")
include_directories("${SDL2_INCLUDE_DIR}")
if(OPENGL_INCLUDE_DIR)
include_directories("${OPENGL_INCLUDE_DIR}")
endif()

View File

@ -164,8 +164,7 @@
E8B6B74317EA0B2000E35523 /* IThirdPersonToolSkin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8B6B74117EA0B1500E35523 /* IThirdPersonToolSkin.cpp */; };
E8B6B74617EA0F2600E35523 /* IGrenadeSkin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8B6B74417EA0F1B00E35523 /* IGrenadeSkin.cpp */; };
E8B6B74917EA12EC00E35523 /* IWeaponSkin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8B6B74717EA12E100E35523 /* IWeaponSkin.cpp */; };
E8CF0396178EDA75000683D4 /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8CF0395178EDA75000683D4 /* SDL.framework */; };
E8CF0398178EDA86000683D4 /* SDL_net.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8CF0397178EDA86000683D4 /* SDL_net.framework */; };
E8B93AD518559EC700BD01E1 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8B93AD418559EC600BD01E1 /* SDL2.framework */; };
E8CF039A178EDABD000683D4 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8CF0399178EDABD000683D4 /* OpenAL.framework */; };
E8CF039C178EDAC9000683D4 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E8CF039B178EDAC9000683D4 /* libz.dylib */; };
E8CF03A8178EDF6A000683D4 /* IRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8CF03A6178EDF6A000683D4 /* IRenderer.cpp */; };
@ -239,7 +238,6 @@
E8EE08A117B8F4B000631987 /* GLRadiosityRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8EE089F17B8F4B000631987 /* GLRadiosityRenderer.cpp */; };
E8F74CE5183F86AE0085AA54 /* View.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8F74CE3183F86AE0085AA54 /* View.cpp */; };
E8F74CE9183F8B9D0085AA54 /* Runner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8F74CE7183F8B9D0085AA54 /* Runner.cpp */; };
E8F74CEB183F92DA0085AA54 /* SDLmain.m in Sources */ = {isa = PBXBuildFile; fileRef = E8F74CEA183F92DA0085AA54 /* SDLmain.m */; };
E8F74CEF183FBA9C0085AA54 /* MainScreenHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8F74CED183FBA9C0085AA54 /* MainScreenHelper.cpp */; };
E8F74CF2183FBB070085AA54 /* MainScreenHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8F74CF0183FBB070085AA54 /* MainScreenHelper.cpp */; };
E8F74CF51840D4CC0085AA54 /* Config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8F74CF31840D4CC0085AA54 /* Config.cpp */; };
@ -619,9 +617,8 @@
E8B6B74517EA0F1E00E35523 /* IGrenadeSkin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IGrenadeSkin.h; sourceTree = "<group>"; };
E8B6B74717EA12E100E35523 /* IWeaponSkin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IWeaponSkin.cpp; sourceTree = "<group>"; };
E8B6B74817EA12E500E35523 /* IWeaponSkin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWeaponSkin.h; sourceTree = "<group>"; };
E8B93AD418559EC600BD01E1 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../../Library/Frameworks/SDL2.framework; sourceTree = "<group>"; };
E8CF0385178ED9D2000683D4 /* OpenSpades */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = OpenSpades; sourceTree = BUILT_PRODUCTS_DIR; };
E8CF0395178EDA75000683D4 /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = ../../../../../Library/Frameworks/SDL.framework; sourceTree = "<group>"; };
E8CF0397178EDA86000683D4 /* SDL_net.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL_net.framework; path = ../../../../../Library/Frameworks/SDL_net.framework; sourceTree = "<group>"; };
E8CF0399178EDABD000683D4 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; };
E8CF039B178EDAC9000683D4 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
E8CF03A6178EDF6A000683D4 /* IRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IRenderer.cpp; path = Sources/Client/IRenderer.cpp; sourceTree = SOURCE_ROOT; };
@ -791,8 +788,7 @@
E8CF03C5178EE78C000683D4 /* OpenGL.framework in Frameworks */,
E8CF039C178EDAC9000683D4 /* libz.dylib in Frameworks */,
E8CF039A178EDABD000683D4 /* OpenAL.framework in Frameworks */,
E8CF0398178EDA86000683D4 /* SDL_net.framework in Frameworks */,
E8CF0396178EDA75000683D4 /* SDL.framework in Frameworks */,
E8B93AD518559EC700BD01E1 /* SDL2.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1235,6 +1231,7 @@
E8CF037C178ED9D2000683D4 = {
isa = PBXGroup;
children = (
E8B93AD418559EC600BD01E1 /* SDL2.framework */,
E844888617D26699005105D0 /* libcurl.dylib */,
E8E446AB17A02EC700BE8855 /* Supporting Files */,
E8E44691179CE7B800BE8855 /* libpng15.15.dylib */,
@ -1249,8 +1246,6 @@
E8CF03C4178EE78C000683D4 /* OpenGL.framework */,
E8CF039B178EDAC9000683D4 /* libz.dylib */,
E8CF0399178EDABD000683D4 /* OpenAL.framework */,
E8CF0397178EDA86000683D4 /* SDL_net.framework */,
E8CF0395178EDA75000683D4 /* SDL.framework */,
E8CF041817905609000683D4 /* Resources */,
E8CF0387178ED9D2000683D4 /* Sources */,
E8CF0386178ED9D2000683D4 /* Products */,
@ -1873,7 +1868,6 @@
E844888417D2633C005105D0 /* json_writer.cpp in Sources */,
E844888A17D39CD3005105D0 /* ErrorDialog.cpp in Sources */,
E8D2ACE317D704E600BE5490 /* GLColorCorrectionFilter.cpp in Sources */,
E8F74CEB183F92DA0085AA54 /* SDLmain.m in Sources */,
E8B6B68F17DE27B500E35523 /* as_atomic.cpp in Sources */,
E8B6B69017DE27B500E35523 /* as_builder.cpp in Sources */,
E8B6B69117DE27B500E35523 /* as_bytecode.cpp in Sources */,
@ -1979,8 +1973,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
/Library/Frameworks/SDL.framework/Headers,
/Library/Frameworks/SDL_net.framework/Headers,
/Library/Frameworks/SDL2.framework/Headers,
/usr/local/include/FL/images,
/usr/local/include,
Sources/ENet/include,
@ -2024,8 +2017,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
/Library/Frameworks/SDL.framework/Headers,
/Library/Frameworks/SDL_net.framework/Headers,
/Library/Frameworks/SDL2.framework/Headers,
/usr/local/include/FL/images,
/usr/local/include,
Sources/ENet/include,
@ -2043,6 +2035,10 @@
E8CF038F178ED9D2000683D4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(LOCAL_LIBRARY_DIR)/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
/opt/X11/lib,
@ -2054,6 +2050,10 @@
E8CF0390178ED9D2000683D4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(LOCAL_LIBRARY_DIR)/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
/opt/X11/lib,

View File

@ -77,5 +77,73 @@
renderer.DrawImage(image, AABB2(x + border, y + border, w - border - border, h - border - border),
AABB2(border, border, iw - border - border, ih - border - border));
}
/** Returns the byte index for a certain character index. */
int GetByteIndexForString(string s, int charIndex, int start = 0) {
int len = s.length;
while(start < len && charIndex > 0) {
int c = s[start];
if((c & 0x80) == 0) {
charIndex--;
start += 1;
}else if((c & 0xe0) == 0xc0) {
charIndex--;
start += 2;
}else if((c & 0xf0) == 0xe0) {
charIndex--;
start += 3;
}else if((c & 0xf8) == 0xf0) {
charIndex--;
start += 4;
}else if((c & 0xfc) == 0xf8) {
charIndex--;
start += 5;
}else if((c & 0xfe) == 0xfc) {
charIndex--;
start += 6;
}else{
// invalid!
charIndex--;
start++;
}
}
if(start > len) start = len;
return start;
}
/** Returns the byte index for a certain character index. */
int GetCharIndexForString(string s, int byteIndex, int start = 0) {
int len = s.length;
int charIndex = 0;
while(start < len && start < byteIndex && byteIndex > 0) {
int c = s[start];
if((c & 0x80) == 0) {
charIndex++;
start += 1;
}else if((c & 0xe0) == 0xc0) {
charIndex++;
start += 2;
}else if((c & 0xf0) == 0xe0) {
charIndex++;
start += 3;
}else if((c & 0xf8) == 0xf0) {
charIndex++;
start += 4;
}else if((c & 0xfc) == 0xf8) {
charIndex++;
start += 5;
}else if((c & 0xfe) == 0xfc) {
charIndex++;
start += 6;
}else{
// invalid!
charIndex++;
start++;
}
}
return charIndex;
}
}

View File

@ -54,12 +54,28 @@ namespace spades {
manager.MouseEvent(x, y);
}
void WheelEvent(float x, float y) {
manager.WheelEvent(x, y);
}
void KeyEvent(string key, bool down) {
manager.KeyEvent(key, down);
}
void CharEvent(string text) {
manager.CharEvent(text);
void TextInputEvent(string text) {
manager.TextInputEvent(text);
}
void TextEditingEvent(string text, int start, int len) {
manager.TextEditingEvent(text, start, len);
}
bool AcceptsTextInput() {
return manager.AcceptsTextInput;
}
AABB2 GetTextInputRect() {
return manager.TextInputRect;
}
void RunFrame(float dt) {

View File

@ -73,12 +73,28 @@ namespace spades {
manager.MouseEvent(x, y);
}
void WheelEvent(float x, float y) {
manager.WheelEvent(x, y);
}
void KeyEvent(string key, bool down) {
manager.KeyEvent(key, down);
}
void CharEvent(string text) {
manager.CharEvent(text);
void TextInputEvent(string text) {
manager.TextInputEvent(text);
}
void TextEditingEvent(string text, int start, int len) {
manager.TextEditingEvent(text, start, len);
}
bool AcceptsTextInput() {
return manager.AcceptsTextInput;
}
AABB2 GetTextInputRect() {
return manager.TextInputRect;
}
private SceneDefinition SetupCamera(SceneDefinition sceneDef,

View File

@ -385,6 +385,20 @@ namespace spades {
return true;
}
AABB2 TextInputRect {
get {
Vector2 textPos = TextOrigin;
Vector2 siz = this.Size;
string text = Text;
int cursorPos = CursorPosition;
Font@ font = this.Font;
float width = font.Measure(text.substr(0, cursorPos)).x;
float fontHeight = font.Measure("A").y;
return AABB2(textPos.x + width, textPos.y,
siz.x - textPos.x - width, fontHeight);
}
}
private int PointToCharIndex(float x) {
x -= TextOrigin.x;
if(x < 0.f) return 0;
@ -394,17 +408,22 @@ namespace spades {
float lastWidth = 0.f;
Font@ font = this.Font;
// FIXME: use binary search for better performance?
// FIXME: support multi-byte charset
int idx = 0;
for(int i = 1; i <= len; i++) {
float width = font.Measure(text.substr(0, i)).x;
int lastIdx = idx;
idx = GetByteIndexForString(text, 1, idx);
float width = font.Measure(text.substr(0, idx)).x;
if(width > x) {
if(x < (lastWidth + width) * 0.5f) {
return i - 1;
return lastIdx;
} else {
return i;
return idx;
}
}
lastWidth = width;
if(idx >= len) {
return len;
}
}
return len;
}
@ -429,7 +448,10 @@ namespace spades {
if(SelectionLength > 0) {
SelectedText = "";
} else {
Select(SelectionStart - 1, 1);
int pos = CursorPosition;
int cIdx = GetCharIndexForString(Text, CursorPosition);
int bIdx = GetByteIndexForString(Text, cIdx - 1);
Select(bIdx, pos - bIdx);
SelectedText = "";
}
OnChanged();
@ -457,8 +479,8 @@ namespace spades {
CursorPosition = ClampCursorPosition(CursorPosition - 1);
}else {
if(SelectionLength == 0) {
// FIXME: support multi-byte charset
Select(CursorPosition - 1);
int cIdx = GetCharIndexForString(Text, CursorPosition);
Select(GetByteIndexForString(Text, cIdx - 1));
} else {
Select(SelectionStart);
}
@ -469,8 +491,8 @@ namespace spades {
CursorPosition = ClampCursorPosition(CursorPosition + 1);
}else {
if(SelectionLength == 0) {
// FIXME: support multi-byte charset
Select(CursorPosition + 1);
int cIdx = GetCharIndexForString(Text, CursorPosition);
Select(GetByteIndexForString(Text, cIdx + 1));
} else {
Select(SelectionEnd);
}
@ -555,6 +577,14 @@ namespace spades {
renderer.DrawImage(img, AABB2(x - 1.f, y, 2, h));
}
void DrawEditingLine(float x, float y, float w, float h) {
Renderer@ renderer = Manager.Renderer;
renderer.ColorNP = Vector4(1.f, 1.f, 1.f, .3f);
Image@ img = renderer.RegisterImage("Gfx/White.tga");
renderer.DrawImage(img, AABB2(x, y + h, w, 2.f));
}
void Render() {
Renderer@ renderer = Manager.Renderer;
Vector2 pos = ScreenPosition;
@ -563,6 +593,20 @@ namespace spades {
Vector2 textPos = TextOrigin + pos;
string text = Text;
string composition = this.EditingText;
int editStart = this.TextEditingRangeStart;
int editLen = this.TextEditingRangeLength;
int markStart = SelectionStart;
int markEnd = SelectionEnd;
if(composition.length > 0){
this.SelectedText = "";
markStart = SelectionStart + editStart;
markEnd = markStart + editLen;
text = text.substr(0, SelectionStart) + composition + text.substr(SelectionStart);
}
if(text.length == 0){
if(IsEnabled) {
font.Draw(Placeholder, textPos, TextScale, PlaceholderColor);
@ -575,8 +619,8 @@ namespace spades {
float fontHeight = font.Measure("A").y;
// draw selection
int start = SelectionStart;
int end = SelectionEnd;
int start = markStart;
int end = markEnd;
if(end == start) {
float x = font.Measure(text.substr(0, start)).x;
DrawBeam(x + textPos.x, textPos.y, fontHeight);
@ -585,7 +629,18 @@ namespace spades {
float x2 = font.Measure(text.substr(0, end)).x;
DrawHighlight(textPos.x + x1, textPos.y, x2 - x1, fontHeight);
}
// draw composition underline
if(composition.length > 0) {
start = SelectionStart;
end = start + composition.length;
float x1 = font.Measure(text.substr(0, start)).x;
float x2 = font.Measure(text.substr(0, end)).x;
DrawEditingLine(textPos.x + x1, textPos.y, x2 - x1, fontHeight);
}
}
}
}

View File

@ -36,6 +36,11 @@ namespace spades {
bool IsControlPressed = false;
bool IsShiftPressed = false;
// IME (Input Method Editor) support
string EditingText;
int EditingStart = 0;
int EditingLength = 0;
private UIElement@ mouseCapturedElement;
private UIElement@ mouseHoverElement;
@ -98,6 +103,13 @@ namespace spades {
return e.Cursor;
}
void WheelEvent(float x, float y) {
UIElement@ e = GetMouseActiveElement();
if(e !is null) {
e.MouseWheel(y);
}
}
void MouseEvent(float x, float y) {
MouseCursorPosition = Vector2(
Clamp(MouseCursorPosition.x + x, 0.f, renderer.ScreenWidth),
@ -128,6 +140,9 @@ namespace spades {
void KeyPanic() {
keyRepeater.KeyUp();
charRepeater.KeyUp();
EditingText = "";
EditingStart = 0;
EditingLength = 0;
}
void KeyEvent(string key, bool down) {
@ -193,14 +208,55 @@ namespace spades {
}
}
void CharEvent(string chr) {
void TextInputEvent(string chr) {
charRepeater.KeyDown(chr);
HandleCharInner(chr);
}
void TextEditingEvent(string chr, int start, int len) {
EditingText = chr;
EditingStart = GetByteIndexForString(chr, start);
EditingLength = GetByteIndexForString(chr, len, EditingStart) - EditingStart;
}
bool AcceptsTextInput {
get {
if(ActiveElement is null) {
EditingText = "";
EditingStart = 0;
EditingLength = 0;
}
return ActiveElement !is null;
}
}
AABB2 TextInputRect {
get {
UIElement@ e = ActiveElement;
if(e !is null) {
AABB2 rt = e.TextInputRect;
Vector2 off = e.ScreenPosition;
rt.min += off;
rt.max += off;
return rt;
}else{
return AABB2();
}
}
}
private void HandleKeyInner(string key) {
{
UIElement@ e = ActiveElement;
if(EditingText.length > 0) {
// now text is being composed by IME.
// ignore some keys to resolve confliction.
if(key == "Escape" || key == "BackSpace" || key == "Left" || key == "Right" ||
key == "Space" || key == "Enter" || key == "Up" || key == "Down" ||
key == "Tab") {
return;
}
}
if(e !is null) {
e.KeyDown(key);
} else {
@ -615,6 +671,44 @@ namespace spades {
}
}
// IME supports
AABB2 TextInputRect {
get {
return AABB2(0.f, 0.f, Size.x, Size.y);
}
}
int TextEditingRangeStart {
get final {
if(this.IsFocused) {
return Manager.EditingStart;
}else{
return 0;
}
}
}
int TextEditingRangeLength {
get final {
if(this.IsFocused) {
return Manager.EditingLength;
}else{
return 0;
}
}
}
string EditingText {
get final {
if(this.IsFocused) {
return Manager.EditingText;
}else{
return "";
}
}
}
void OnResized() {
}

View File

@ -49,7 +49,7 @@ add_dependencies(OpenSpades Angelscript Angelscript_addons)
if(WIN32)
source_group("Resources" ${RESOURCE_FILES})
foreach(LIB ${SDL_LIBRARY})
foreach(LIB ${SDL2_LIBRARY})
string(REGEX REPLACE "\\.lib$" ".dll" SDL_DLL ${LIB})
if(EXISTS "${SDL_DLL}")
add_custom_command(TARGET OpenSpades POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SDL_DLL}" "${CMAKE_BINARY_DIR}/bin/\$\(Configuration\)/")
@ -87,7 +87,7 @@ source_group("ScriptBindings" FILES ${SCRIPTBINDING_FILES})
source_group("libs\\unzip" FILES ${UNZIP_FILES})
target_link_libraries(OpenSpades ${SDL_LIBRARY} ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} ${FLTK_OS_LIBS} ${ZLIB_LIBRARIES} ${CURL_LIBRARY} ${CMAKE_DL_LIBS} ${ANGELSCRIPT_LIBS})
target_link_libraries(OpenSpades ${SDL2_LIBRARY} ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} ${FLTK_OS_LIBS} ${ZLIB_LIBRARIES} ${CURL_LIBRARY} ${CMAKE_DL_LIBS} ${ANGELSCRIPT_LIBS})
#todo: MACOSX_BUNDLE_ICON_FILE ?

View File

@ -918,20 +918,63 @@ namespace spades {
}
}
void Client::CharEvent(const std::string &ch){
void Client::WheelEvent(float x, float y) {
SPADES_MARK_FUNCTION();
if(scriptedUI->NeedsInput()) {
scriptedUI->CharEvent(ch);
scriptedUI->WheelEvent(x, y);
return;
}
if(y > .5f) {
KeyEvent("WheelDown", true);
KeyEvent("WheelDown", false);
}else if(y < -.5f){
KeyEvent("WheelUp", true);
KeyEvent("WheelUp", false);
}
}
void Client::TextInputEvent(const std::string &ch){
SPADES_MARK_FUNCTION();
if(scriptedUI->NeedsInput()) {
scriptedUI->TextInputEvent(ch);
return;
}
if(ch == "/") {
scriptedUI->EnterCommandWindow();
}
}
void Client::TextEditingEvent(const std::string &ch,
int start, int len) {
SPADES_MARK_FUNCTION();
if(scriptedUI->NeedsInput()) {
scriptedUI->TextEditingEvent(ch, start, len);
return;
}
}
bool Client::AcceptsTextInput() {
SPADES_MARK_FUNCTION();
if(scriptedUI->NeedsInput()) {
return scriptedUI->AcceptsTextInput();
}
return false;
}
AABB2 Client::GetTextInputRect() {
SPADES_MARK_FUNCTION();
if(scriptedUI->NeedsInput()) {
return scriptedUI->GetTextInputRect();
}
return AABB2();
}
// TODO: this might not be a fast way
//lm: ideally we should normalize the key when reading the config.
static bool CheckKey(const std::string& cfg,

View File

@ -235,9 +235,14 @@ namespace spades {
virtual void Closing();
virtual void MouseEvent(float x, float y);
virtual void WheelEvent(float x, float y);
virtual void KeyEvent(const std::string&,
bool down);
virtual void CharEvent(const std::string&);
bool down);
virtual void TextInputEvent(const std::string&);
virtual void TextEditingEvent(const std::string&,
int start, int len);
virtual bool AcceptsTextInput();
virtual AABB2 GetTextInputRect();
void SetWorld(World *);
World *GetWorld() const { return world; }

View File

@ -87,6 +87,20 @@ namespace spades {
c.ExecuteChecked();
}
void ClientUI::WheelEvent(float x, float y) {
SPADES_MARK_FUNCTION();
if(!ui){
return;
}
static ScriptFunction func("ClientUI", "void WheelEvent(float, float)");
ScriptContextHandle c = func.Prepare();
c->SetObject(&*ui);
c->SetArgFloat(0, x);
c->SetArgFloat(1, y);
c.ExecuteChecked();
}
void ClientUI::KeyEvent(const std::string & key, bool down) {
SPADES_MARK_FUNCTION();
if(!ui){
@ -101,12 +115,12 @@ namespace spades {
c.ExecuteChecked();
}
void ClientUI::CharEvent(const std::string &ch) {
void ClientUI::TextInputEvent(const std::string &ch) {
SPADES_MARK_FUNCTION();
if(!ui){
return;
}
static ScriptFunction func("ClientUI", "void CharEvent(string)");
static ScriptFunction func("ClientUI", "void TextInputEvent(string)");
ScriptContextHandle c = func.Prepare();
std::string k = ch;
c->SetObject(&*ui);
@ -114,6 +128,46 @@ namespace spades {
c.ExecuteChecked();
}
void ClientUI::TextEditingEvent(const std::string &ch,
int start, int len) {
SPADES_MARK_FUNCTION();
if(!ui){
return;
}
static ScriptFunction func("ClientUI", "void TextEditingEvent(string,int,int)");
ScriptContextHandle c = func.Prepare();
std::string k = ch;
c->SetObject(&*ui);
c->SetArgObject(0, reinterpret_cast<void*>(&k));
c->SetArgDWord(1, static_cast<asDWORD>(start));
c->SetArgDWord(2, static_cast<asDWORD>(len));
c.ExecuteChecked();
}
bool ClientUI::AcceptsTextInput() {
SPADES_MARK_FUNCTION();
if(!ui){
return false;
}
static ScriptFunction func("ClientUI", "bool AcceptsTextInput()");
ScriptContextHandle c = func.Prepare();
c->SetObject(&*ui);
c.ExecuteChecked();
return c->GetReturnByte() != 0;
}
AABB2 ClientUI::GetTextInputRect() {
SPADES_MARK_FUNCTION();
if(!ui){
return AABB2();
}
static ScriptFunction func("ClientUI", "AABB2 GetTextInputRect()");
ScriptContextHandle c = func.Prepare();
c->SetObject(&*ui);
c.ExecuteChecked();
return *reinterpret_cast<AABB2*>(c->GetReturnObject());
}
bool ClientUI::WantsClientToBeClosed() {
SPADES_MARK_FUNCTION();
if(!ui){

View File

@ -56,9 +56,14 @@ namespace spades {
client::IAudioDevice *GetAudioDevice() { return &*audioDevice; }
void MouseEvent(float x, float y);
void WheelEvent(float x, float y);
void KeyEvent(const std::string&,
bool down);
void CharEvent(const std::string&);
bool down);
void TextInputEvent(const std::string&);
void TextEditingEvent(const std::string&,
int start, int len);
bool AcceptsTextInput();
AABB2 GetTextInputRect();
void RunFrame(float dt);

View File

@ -24,6 +24,7 @@
#include "Debug.h"
#include "ThreadLocalStorage.h"
#include "ConcurrentDispatch.h"
#include <typeinfo>
namespace spades {
@ -90,8 +91,13 @@ namespace spades {
if(threadInfo)
return;
const std::type_info& info = typeid(*this);
const char *name = info.name();
if(name == nullptr)
name = "(null)";
threadId = 0;
threadInfo = SDL_CreateThread(InternalRunner, this);
threadInfo = SDL_CreateThread(InternalRunner, name, this);
}
void Thread::Join() {

View File

@ -90,6 +90,25 @@ namespace spades {
c.ExecuteChecked();
}
void MainScreen::WheelEvent(float x, float y) {
SPADES_MARK_FUNCTION();
if(subview){
subview->WheelEvent(x, y);
return;
}
if(!ui){
return;
}
static ScriptFunction func("MainScreenUI", "void WheelEvent(float, float)");
ScriptContextHandle c = func.Prepare();
c->SetObject(&*ui);
c->SetArgFloat(0, x);
c->SetArgFloat(1, y);
c.ExecuteChecked();
}
void MainScreen::KeyEvent(const std::string & key, bool down) {
SPADES_MARK_FUNCTION();
if(subview){
@ -108,16 +127,16 @@ namespace spades {
c.ExecuteChecked();
}
void MainScreen::CharEvent(const std::string &ch) {
void MainScreen::TextInputEvent(const std::string &ch) {
SPADES_MARK_FUNCTION();
if(subview){
subview->CharEvent(ch);
subview->TextInputEvent(ch);
return;
}
if(!ui){
return;
}
static ScriptFunction func("MainScreenUI", "void CharEvent(string)");
static ScriptFunction func("MainScreenUI", "void TextInputEvent(string)");
ScriptContextHandle c = func.Prepare();
std::string k = ch;
c->SetObject(&*ui);
@ -125,6 +144,56 @@ namespace spades {
c.ExecuteChecked();
}
void MainScreen::TextEditingEvent(const std::string &ch,
int start, int len) {
SPADES_MARK_FUNCTION();
if(subview){
subview->TextEditingEvent(ch, start, len);
return;
}
if(!ui){
return;
}
static ScriptFunction func("MainScreenUI", "void TextEditingEvent(string, int, int)");
ScriptContextHandle c = func.Prepare();
std::string k = ch;
c->SetObject(&*ui);
c->SetArgObject(0, reinterpret_cast<void*>(&k));
c->SetArgDWord(1, static_cast<asDWORD>(start));
c->SetArgDWord(2, static_cast<asDWORD>(len));
c.ExecuteChecked();
}
bool MainScreen::AcceptsTextInput() {
SPADES_MARK_FUNCTION();
if(subview){
return subview->AcceptsTextInput();
}
if(!ui){
return false;
}
static ScriptFunction func("MainScreenUI", "bool AcceptsTextInput()");
ScriptContextHandle c = func.Prepare();
c->SetObject(&*ui);
c.ExecuteChecked();
return c->GetReturnByte() != 0;
}
AABB2 MainScreen::GetTextInputRect() {
SPADES_MARK_FUNCTION();
if(subview){
return subview->GetTextInputRect();
}
if(!ui){
return AABB2();
}
static ScriptFunction func("MainScreenUI", "AABB2 GetTextInputRect()");
ScriptContextHandle c = func.Prepare();
c->SetObject(&*ui);
c.ExecuteChecked();
return *reinterpret_cast<AABB2*>(c->GetReturnObject());
}
bool MainScreen::WantsToBeClosed() {
SPADES_MARK_FUNCTION();
if(!ui){

View File

@ -60,7 +60,12 @@ namespace spades {
virtual void MouseEvent(float x, float y);
virtual void KeyEvent(const std::string&,
bool down);
virtual void CharEvent(const std::string&);
virtual void TextInputEvent(const std::string&);
virtual void TextEditingEvent(const std::string&,
int start, int len);
virtual bool AcceptsTextInput();
virtual AABB2 GetTextInputRect();
virtual void WheelEvent(float x, float y);
virtual void RunFrame(float dt);

View File

@ -383,23 +383,41 @@ void MainWindow::CheckGLCapability() {
SPLog("Initializing SDL for capability query");
SDL_Init(SDL_INIT_VIDEO);
SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN |
SDL_DOUBLEBUF);
if(modes && modes != (SDL_Rect **)-1){
g_modes.clear();
for(size_t i = 0; modes[i]; i++){
SDL_Rect mode = *(modes[i]);
int idDisplay = 0;
int numDisplayMode = SDL_GetNumDisplayModes(idDisplay);
SDL_DisplayMode mode;
g_modes.clear();
if(numDisplayMode > 0){
for(int i = 0; i < numDisplayMode; i++) {
SDL_GetDisplayMode(idDisplay, i, &mode);
if(mode.w < 800 || mode.h < 600)
continue;
g_modes.push_back(spades::IntVector3::Make(mode.w, mode.h, 0));
SPLog("Video Mode Found: %dx%d", mode.w, mode.h);
}
}else{
SPLog("Failed to get video mode list. Presetting default list");
g_modes.push_back(spades::IntVector3::Make(800, 600, 0));
g_modes.push_back(spades::IntVector3::Make(1024, 768, 0));
g_modes.push_back(spades::IntVector3::Make(1280, 720, 0));
g_modes.push_back(spades::IntVector3::Make(1920, 1080, 0));
}
bool capable = true;
std::string msg;
if(!SDL_SetVideoMode(1,1, 32, SDL_OPENGL|SDL_NOFRAME)){
SDL_Window *window = SDL_CreateWindow("Querying OpenGL Capabilities",
1, 1, 1, 1,
SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS);
if(window == nullptr) {
SPLog("Failed to create SDL window: %s", SDL_GetError());
}
SDL_GLContext context = window ? SDL_GL_CreateContext(window) : nullptr;
if(window != nullptr && context == nullptr) {
SPLog("Failed to create OpenGL context: %s", SDL_GetError());
}
if(!context){
// OpenGL initialization failed!
outputGLRenderer->value("N/A");
outputGLVersion->value("N/A");
@ -417,6 +435,8 @@ void MainWindow::CheckGLCapability() {
particleHighCapable = false;
}else{
SDL_GL_MakeCurrent(window, context);
shaderHighCapable = true;
postFilterHighCapable = true;
particleHighCapable = true;
@ -685,14 +705,16 @@ void MainWindow::CheckGLCapability() {
" You cannot play OpenSpades.</b><br>&nbsp;<br>" + msg;
}
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
SPLog("SDL Capability Query Window finalized");
}
msg = "<font face=Helvetica>" + msg + "</font><a name=last></a>";
glInfoView->value(msg.c_str());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
SPLog("SDL video subsystem finalized");
SPLog("System is OpenSpades capable: %s",
capable ? "YES": "NO");

View File

@ -197,6 +197,9 @@ namespace spades {
}
}
// TODO: support text inputing for cg_smp runner.
// see SDLRunner.cpp and grep SDL_StartTextInput
//Fl::check();
}

View File

@ -123,8 +123,19 @@ ReportError(err, __LINE__, __PRETTY_FUNCTION__); \
func);
}
SDLGLDevice::SDLGLDevice(SDL_Surface *s):
surface(s) {
SDLGLDevice::SDLGLDevice(SDL_Window *s):
window(s) {
SDL_GetWindowSize(window, &w, &h);
context = SDL_GL_CreateContext(s);
if(!context) {
const char *err = SDL_GetError();
SPLog("Failed to create GL context!: %s", err);
SPRaise("Failed to create GL context: %s", err);
}
SDL_GL_MakeCurrent(window, context);
#ifndef __APPLE__
GLenum err = glewInit();
if (GLEW_OK != err){
@ -150,6 +161,10 @@ ReportError(err, __LINE__, __PRETTY_FUNCTION__); \
while(glGetError() != GL_NO_ERROR);
}
SDLGLDevice::~SDLGLDevice() {
SDL_GL_DeleteContext(context);
}
void SDLGLDevice::DepthRange(Float near, Float far){
CheckExistence(glDepthRange);
glDepthRange(near, far);
@ -188,7 +203,7 @@ ReportError(err, __LINE__, __PRETTY_FUNCTION__); \
void SDLGLDevice::Swap() {
//glFinish();
CheckErrorAlways();
SDL_GL_SwapBuffers();
SDL_GL_SwapWindow(window);
#if 0
Uint32 t = SDL_GetTicks();
if(lastFrame == 0) t = lastFrame - 30;
@ -2216,11 +2231,11 @@ ReportError(err, __LINE__, __PRETTY_FUNCTION__); \
IGLDevice::Integer SDLGLDevice::ScreenWidth() {
return surface->w;
return w;
}
IGLDevice::Integer SDLGLDevice::ScreenHeight() {
return surface->h;
return h;
}
}

View File

@ -27,9 +27,12 @@
namespace spades {
namespace gui {
class SDLGLDevice: public draw::IGLDevice {
SDL_Surface *surface;
SDL_Window *window;
SDL_GLContext context;
int w, h;
public:
SDLGLDevice(SDL_Surface *);
SDLGLDevice(SDL_Window *);
virtual ~SDLGLDevice();
virtual void DepthRange(Float near, Float far);
virtual void Viewport(Integer x, Integer y,

View File

@ -54,13 +54,13 @@ namespace spades {
case SDL_BUTTON_LEFT: return "LeftMouseButton";
case SDL_BUTTON_RIGHT: return "RightMouseButton";
case SDL_BUTTON_MIDDLE: return "MiddleMouseButton";
case SDL_BUTTON_WHEELUP: return "WheelUp";
case SDL_BUTTON_WHEELDOWN: return "WheelDown";
case SDL_BUTTON_X1: return "MouseButton4";
case SDL_BUTTON_X2: return "MouseButton5";
default: return std::string();
}
}
std::string SDLRunner::TranslateKey(const SDL_keysym &k){
std::string SDLRunner::TranslateKey(const SDL_Keysym &k){
SPADES_MARK_FUNCTION();
@ -118,19 +118,10 @@ namespace spades {
view->MouseEvent(event.motion.xrel, event.motion.yrel);
}
break;
case SDL_MOUSEWHEEL:
view->WheelEvent(-event.wheel.x, -event.wheel.y);
break;
case SDL_KEYDOWN:
if(event.key.keysym.unicode){
int uni = event.key.keysym.unicode;
if(uni > 0 && uni < 128 &&
uni != 8 && // no backspace
uni != 27 && // no escape
uni != 13 && uni != 10 &&
uni != 127){ // no enter
std::string s;
s += (char)uni;
view->CharEvent(s);
}
}
view->KeyEvent(TranslateKey(event.key.keysym),
true);
break;
@ -138,6 +129,13 @@ namespace spades {
view->KeyEvent(TranslateKey(event.key.keysym),
false);
break;
case SDL_TEXTINPUT:
view->TextInputEvent(event.text.text);
break;
case SDL_TEXTEDITING:
view->TextEditingEvent(event.edit.text, event.edit.start, event.edit.length);
break;
/* TODO: activation/deactivation
case SDL_ACTIVEEVENT:
if( event.active.state & (SDL_APPACTIVE|SDL_APPINPUTFOCUS) ) { //any of the 2 is good
if(event.active.gain){
@ -150,7 +148,7 @@ namespace spades {
SDL_ShowCursor(1);
}
}
break;
break;*/
default:
break;
}
@ -166,6 +164,7 @@ namespace spades {
bool lastShift = false;
bool lastCtrl = false;
bool editing = false;
SPLog("Starting Client Loop");
@ -212,6 +211,23 @@ namespace spades {
}
}
bool ed = view->AcceptsTextInput();
if(ed && !editing) {
SDL_StartTextInput();
}else if(!ed && editing){
SDL_StopTextInput();
}
editing = ed;
if(editing){
AABB2 rt = view->GetTextInputRect();
SDL_Rect srt;
srt.x = (int)rt.GetMinX();
srt.y = (int)rt.GetMinY();
srt.w = (int)rt.GetWidth();
srt.h = (int)rt.GetHeight();
SDL_SetTextInputRect(&srt);
}
while(SDL_PollEvent(&event)) {
ProcessEvent(event, view);
}
@ -226,44 +242,45 @@ namespace spades {
SPADES_MARK_FUNCTION();
SDL_Init(SDL_INIT_VIDEO);
try{
std::string caption;
{
std::string pkg = PACKAGE_STRING;
caption = PACKAGE_STRING;
#if !NDEBUG
pkg.append( " DEBUG build" );
caption.append( " DEBUG build" );
#endif
#ifdef OPENSPADES_COMPILER_STR
pkg.append( " " OPENSPADES_COMPILER_STR ); //add compiler to window title
caption.append( " " OPENSPADES_COMPILER_STR ); //add compiler to window title
#endif
SDL_WM_SetCaption(pkg.c_str(), pkg.c_str());
}
SDL_Surface *surface;
SDL_Window *window;
int sdlFlags = SDL_OPENGL | SDL_DOUBLEBUF;
Uint32 sdlFlags;
sdlFlags = SDL_WINDOW_OPENGL;
if(r_fullscreen)
sdlFlags |= SDL_FULLSCREEN;
sdlFlags |= SDL_WINDOW_FULLSCREEN;
// OpenGL core profile: supported by SDL 1.3 or later
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, r_depthBits);
surface = SDL_SetVideoMode(r_videoWidth, r_videoHeight, r_colorBits, sdlFlags);
if(!surface){
window = SDL_CreateWindow(caption.c_str(),
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
r_videoWidth, r_videoHeight, sdlFlags);
if(!window){
std::string msg = SDL_GetError();
SPRaise("Failed to initialize video device: %s", msg.c_str());
SPRaise("Failed to create graphics window: %s", msg.c_str());
}
SDL_WM_GrabInput(SDL_GRAB_ON);
SDL_EnableUNICODE(1);
SDL_SetRelativeMouseMode(SDL_TRUE);
SDL_ShowCursor(0);
mActive = true;
{
SDLGLDevice glDevice(surface);
SDLGLDevice glDevice(window);
Handle<draw::GLRenderer> renderer(new draw::GLRenderer(&glDevice), false);
audio::ALDevice audio;

View File

@ -35,7 +35,7 @@ namespace spades {
class SDLRunner: public IRunnable {
bool mActive;
protected:
std::string TranslateKey(const SDL_keysym&);
std::string TranslateKey(const SDL_Keysym&);
std::string TranslateButton(Uint8 b);
virtual int GetModState();
void ProcessEvent(SDL_Event& event,

View File

@ -21,6 +21,7 @@
#pragma once
#include <Core/RefCountedObject.h>
#include <Core/Math.h>
namespace spades {
namespace gui {
@ -33,7 +34,12 @@ namespace spades {
virtual void MouseEvent(float x, float y) {}
virtual void KeyEvent(const std::string&,
bool down) {}
virtual void CharEvent(const std::string&) {}
virtual void TextInputEvent(const std::string&) {}
virtual void TextEditingEvent(const std::string&,
int start, int len) {}
virtual bool AcceptsTextInput() { return false; }
virtual AABB2 GetTextInputRect() { return AABB2(); }
virtual void WheelEvent(float x, float y) {}
virtual void RunFrame(float dt) {}