diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
index e95326468..2833cf1d1 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
@@ -174,9 +174,9 @@ namespace Barotrauma
Character.DisableControls = true;
- if (PlayerInput.KeyHit(Keys.Tab))
+ if (PlayerInput.KeyHit(Keys.Tab) && !textBox.IsIMEActive)
{
- textBox.Text = AutoComplete(textBox.Text, increment: string.IsNullOrEmpty(currentAutoCompletedCommand) ? 0 : 1 );
+ textBox.Text = AutoComplete(textBox.Text, increment: string.IsNullOrEmpty(currentAutoCompletedCommand) ? 0 : 1 );
}
if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl))
diff --git a/Barotrauma/BarotraumaClient/ClientSource/EventInput/EventInput.cs b/Barotrauma/BarotraumaClient/ClientSource/EventInput/EventInput.cs
index fdf54af3f..be47a0793 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/EventInput/EventInput.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/EventInput/EventInput.cs
@@ -1,72 +1,18 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
-using System;
-using System.Runtime.InteropServices;
-using System.Text;
namespace EventInput
{
- public class CharacterEventArgs : EventArgs
+ public readonly record struct CharacterEventArgs(char Character, long Param)
{
- private readonly char character;
- private readonly long lParam;
-
- public CharacterEventArgs(char character, long lParam)
- {
- this.character = character;
- this.lParam = lParam;
- }
-
- public char Character
- {
- get { return character; }
- }
-
- public long Param
- {
- get { return lParam; }
- }
-
- public long RepeatCount
- {
- get { return lParam & 0xffff; }
- }
-
- public bool ExtendedKey
- {
- get { return (lParam & (1 << 24)) > 0; }
- }
-
- public bool AltPressed
- {
- get { return (lParam & (1 << 29)) > 0; }
- }
-
- public bool PreviousState
- {
- get { return (lParam & (1 << 30)) > 0; }
- }
-
- public bool TransitionState
- {
- get { return (lParam & (1 << 31)) > 0; }
- }
+ public long RepeatCount => Param & 0xffff;
+ public bool ExtendedKey => (Param & (1 << 24)) > 0;
+ public bool AltPressed => (Param & (1 << 29)) > 0;
+ public bool PreviousState => (Param & (1 << 30)) > 0;
+ public bool TransitionState => (Param & (1 << 31)) > 0;
}
- public class KeyEventArgs : EventArgs
- {
- private Keys keyCode;
-
- public KeyEventArgs(Keys keyCode)
- {
- this.keyCode = keyCode;
- }
-
- public Keys KeyCode
- {
- get { return keyCode; }
- }
- }
+ public readonly record struct KeyEventArgs(Keys KeyCode, char Character);
public delegate void CharEnteredHandler(object sender, CharacterEventArgs e);
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
@@ -89,14 +35,11 @@ namespace EventInput
///
public static event KeyEventHandler KeyUp;
-
-#if !WINDOWS
///
/// Raised when the user is editing text and IME is in progress.
- /// Windows build uses ImeSharp instead because SDL2's IME implementation is broken on Windows (https://github.com/libsdl-org/SDL/issues/2243)
///
public static event EditingTextHandler EditingText;
-#endif
+
static bool initialized;
@@ -110,11 +53,10 @@ namespace EventInput
{
return;
}
-
+
window.TextInput += ReceiveInput;
-#if !WINDOWS
+ window.KeyDown += ReceiveKeyDown;
window.TextEditing += ReceiveTextEditing;
-#endif
initialized = true;
}
@@ -122,15 +64,17 @@ namespace EventInput
private static void ReceiveInput(object sender, TextInputEventArgs e)
{
OnCharEntered(e.Character);
- KeyDown?.Invoke(sender, new KeyEventArgs(e.Key));
}
-#if !WINDOWS
+ private static void ReceiveKeyDown(object sender, TextInputEventArgs e)
+ {
+ KeyDown?.Invoke(sender, new KeyEventArgs(e.Key, e.Character));
+ }
+
private static void ReceiveTextEditing(object sender, TextEditingEventArgs e)
{
EditingText?.Invoke(sender, e);
}
-#endif
public static void OnCharEntered(char character)
{
diff --git a/Barotrauma/BarotraumaClient/ClientSource/EventInput/KeyboardDispatcher.cs b/Barotrauma/BarotraumaClient/ClientSource/EventInput/KeyboardDispatcher.cs
index f18dbcd73..dc25c25d7 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/EventInput/KeyboardDispatcher.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/EventInput/KeyboardDispatcher.cs
@@ -10,11 +10,7 @@ namespace EventInput
void ReceiveTextInput(string text);
void ReceiveCommandInput(char command);
void ReceiveSpecialInput(Keys key);
-
-#if !WINDOWS
- /// Windows build uses ImeSharp instead because SDL2's IME implementation is broken on Windows (https://github.com/libsdl-org/SDL/issues/2243)
- void ReceiveEditingInput(string text, int start);
-#endif
+ void ReceiveEditingInput(string text, int start, int length);
bool Selected { get; set; } //or Focused
}
@@ -26,59 +22,27 @@ namespace EventInput
EventInput.Initialize(window);
EventInput.CharEntered += EventInput_CharEntered;
EventInput.KeyDown += EventInput_KeyDown;
-#if !WINDOWS
EventInput.EditingText += EventInput_TextEditing;
-#endif
-
- /*
- * SDL by default starts in a state where it accepts IME inputs
- * this is bad because this blocks keybinds since the IME thinks
- * it's typing in a text box and not forwarding keybinds to the game.
- */
- TextInput.StopTextInput();
+ GameMain.ResetIMEWorkaround();
}
-#if !WINDOWS
+
public void EventInput_TextEditing(object sender, TextEditingEventArgs e)
{
- _subscriber?.ReceiveEditingInput(e.Text, e.Start);
+ _subscriber?.ReceiveEditingInput(e.Text, e.Start, e.Length);
}
-#endif
+
public void EventInput_KeyDown(object sender, KeyEventArgs e)
{
_subscriber?.ReceiveSpecialInput(e.KeyCode);
+ if (char.IsControl(e.Character))
+ {
+ _subscriber?.ReceiveCommandInput(e.Character);
+ }
}
void EventInput_CharEntered(object sender, CharacterEventArgs e)
{
- if (_subscriber == null)
- return;
- if (char.IsControl(e.Character))
- {
- _subscriber.ReceiveCommandInput(e.Character);
- // Doesn't work as expected. Not sure why this should be run in a separate thread.
- //#if WINDOWS
- // //ctrl-v
- // if (e.Character == 0x16) // 22
- // {
- // //XNA runs in Multiple Thread Apartment state, which cannot recieve clipboard
- // Thread thread = new Thread(PasteThread);
- // thread.SetApartmentState(ApartmentState.STA);
- // thread.Start();
- // thread.Join();
- // _subscriber.ReceiveTextInput(_pasteResult);
- // }
- // else
- // {
- // _subscriber.ReceiveCommandInput(e.Character);
- // }
- //#else
- // _subscriber.ReceiveCommandInput(e.Character);
- //#endif
- }
- else
- {
- _subscriber.ReceiveTextInput(e.Character);
- }
+ _subscriber?.ReceiveTextInput(e.Character);
}
IKeyboardSubscriber _subscriber;
@@ -97,8 +61,9 @@ namespace EventInput
if (value is GUITextBox box)
{
- TextInput.SetTextInputRect(box.Rect);
+ TextInput.SetTextInputRect(box.MouseRect);
TextInput.StartTextInput();
+ TextInput.SetTextInputRect(box.MouseRect);
}
_subscriber = value;
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs
index a0067597c..9a84a1b59 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs
@@ -1261,10 +1261,6 @@ namespace Barotrauma
UpdateMessages(deltaTime);
UpdateSavingIndicator(deltaTime);
}
-
-#if WINDOWS
- GUITextBox.UpdateIME();
-#endif
}
public static void UpdateGUIMessageBoxesOnly(float deltaTime)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIDropDown.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIDropDown.cs
index 465e5d771..dd848bd65 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIDropDown.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIDropDown.cs
@@ -111,10 +111,7 @@ namespace Barotrauma
}
public void ReceiveTextInput(string text) { }
public void ReceiveCommandInput(char command) { }
-
-#if !WINDOWS
- public void ReceiveEditingInput(string text, int start) { }
-#endif
+ public void ReceiveEditingInput(string text, int start, int length) { }
public void ReceiveSpecialInput(Keys key)
{
@@ -125,9 +122,7 @@ namespace Barotrauma
listBox.ReceiveSpecialInput(key);
GUI.KeyboardDispatcher.Subscriber = this;
break;
- case Keys.Enter:
- case Keys.Space:
- case Keys.Escape:
+ default:
GUI.KeyboardDispatcher.Subscriber = null;
break;
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs
index d5f2e3a7e..b03bc27a6 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs
@@ -1347,10 +1347,7 @@ namespace Barotrauma
}
public void ReceiveTextInput(string text) { }
public void ReceiveCommandInput(char command) { }
-
-#if !WINDOWS
- public void ReceiveEditingInput(string text, int start) { }
-#endif
+ public void ReceiveEditingInput(string text, int start, int length) { }
public void ReceiveSpecialInput(Keys key)
{
@@ -1368,9 +1365,7 @@ namespace Barotrauma
case Keys.Right:
if (isHorizontal && AllowArrowKeyScroll) { SelectNext(playSelectSound: PlaySelectSound.Yes); }
break;
- case Keys.Enter:
- case Keys.Space:
- case Keys.Escape:
+ default:
GUI.KeyboardDispatcher.Subscriber = null;
break;
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIPrefab.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIPrefab.cs
index d3ab8134d..c9a7631cb 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIPrefab.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIPrefab.cs
@@ -223,24 +223,24 @@ namespace Barotrauma
public ScalableFont GetFontForStr(string str) =>
Prefabs.ActivePrefab.GetFontForCategory(TextManager.GetSpeciallyHandledCategories(str));
- public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
+ public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects spriteEffects, float layerDepth)
{
- DrawString(sb, text.Value, position, color, rotation, origin, scale, se, layerDepth);
+ DrawString(sb, text.Value, position, color, rotation, origin, scale, spriteEffects, layerDepth);
}
- public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
+ public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects spriteEffects, float layerDepth)
{
- GetFontForStr(text).DrawString(sb, text, position, color, rotation, origin, scale, se, layerDepth);
+ GetFontForStr(text).DrawString(sb, text, position, color, rotation, origin, scale, spriteEffects, layerDepth);
}
- public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, Alignment alignment = Alignment.TopLeft)
+ public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects spriteEffects, float layerDepth, Alignment alignment = Alignment.TopLeft)
{
- DrawString(sb, text.Value, position, color, rotation, origin, scale, se, layerDepth, alignment);
+ DrawString(sb, text.Value, position, color, rotation, origin, scale, spriteEffects, layerDepth, alignment);
}
- public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
+ public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects spriteEffects, float layerDepth, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
{
- GetFontForStr(text).DrawString(sb, text, position, color, rotation, origin, scale, se, layerDepth, alignment, forceUpperCase);
+ GetFontForStr(text).DrawString(sb, text, position, color, rotation, origin, scale, spriteEffects, layerDepth, alignment, forceUpperCase);
}
public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit, bool italics = false)
@@ -253,9 +253,9 @@ namespace Barotrauma
GetFontForStr(text).DrawString(sb, text, position, color, forceUpperCase, italics);
}
- public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, in ImmutableArray? richTextData, int rtdOffset = 0, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
+ public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects spriteEffects, float layerDepth, in ImmutableArray? richTextData, int rtdOffset = 0, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
{
- GetFontForStr(text).DrawStringWithColors(sb, text, position, color, rotation, origin, scale, se, layerDepth, richTextData, rtdOffset, alignment, forceUpperCase);
+ GetFontForStr(text).DrawStringWithColors(sb, text, position, color, rotation, origin, scale, spriteEffects, layerDepth, richTextData, rtdOffset, alignment, forceUpperCase);
}
public Vector2 MeasureString(LocalizedString str, bool removeExtraSpacing = false)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs
index dae60140f..4972e95d3 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs
@@ -256,6 +256,8 @@ namespace Barotrauma
private readonly IMEPreviewTextHandler imePreviewTextHandler;
+ public bool IsIMEActive => imePreviewTextHandler is { HasText: true };
+
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, GUIFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false, bool createPenIcon = true)
: base(style, rectT)
@@ -574,6 +576,7 @@ namespace Barotrauma
{
CaretIndex = Math.Min(Text.Length, CaretIndex + input.Length);
OnTextChanged?.Invoke(this, Text);
+ imePreviewTextHandler?.Reset();
}
}
@@ -602,10 +605,12 @@ namespace Barotrauma
public void ReceiveCommandInput(char command)
{
- if (Text == null) Text = "";
+ if (IsIMEActive) { return; }
+
+ if (Text == null) { Text = ""; }
// Prevent alt gr from triggering any of these as that combination is often needed for special characters
- if (PlayerInput.IsAltDown()) return;
+ if (PlayerInput.IsAltDown()) { return; }
switch (command)
{
@@ -678,21 +683,22 @@ namespace Barotrauma
break;
}
}
-#if !WINDOWS
- public void ReceiveEditingInput(string text, int start)
+
+ public void ReceiveEditingInput(string text, int start, int length)
{
if (string.IsNullOrEmpty(text))
{
- if (start is 0) { imePreviewTextHandler.Reset(); }
+ imePreviewTextHandler.Reset();
return;
}
- imePreviewTextHandler.UpdateText(text, start);
+ imePreviewTextHandler.UpdateText(text, start, length);
}
-#endif
public void ReceiveSpecialInput(Keys key)
{
+ if (IsIMEActive) { return; }
+
switch (key)
{
case Keys.Left:
@@ -883,20 +889,7 @@ namespace Barotrauma
public void DrawIMEPreview(SpriteBatch spriteBatch)
{
- if (!imePreviewTextHandler.HasText) { return; }
-
- Vector2 imePosition = CaretScreenPos;
- int inflate = GUI.IntScale(3);
-
- RectangleF rect = new RectangleF(imePosition, imePreviewTextHandler.TextSize);
- rect.Inflate(inflate, inflate);
-
- RectangleF borderRect = rect;
- borderRect.Inflate(1, 1);
-
- GUI.DrawFilledRectangle(spriteBatch, borderRect, Color.White, depth: 0.02f);
- GUI.DrawFilledRectangle(spriteBatch, rect, Color.Black, depth: 0.01f);
- Font.DrawString(spriteBatch, imePreviewTextHandler.PreviewText, imePosition, GUIStyle.Orange, 0.0f, Vector2.Zero, 1f, SpriteEffects.None, 0, alignment: textBlock.TextAlignment, forceUpperCase: textBlock.ForceUpperCase);
+ imePreviewTextHandler.DrawIMEPreview(spriteBatch, CaretScreenPos, textBlock);
}
private void CalculateSelection()
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBoxIME.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBoxIME.cs
deleted file mode 100644
index ca20e30cf..000000000
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBoxIME.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using ImeSharp;
-using Microsoft.Xna.Framework;
-using System;
-
-namespace Barotrauma;
-
-///
-/// A class for handling Input Method Editor (used for inputting e.g. Chinese and Japanese text)
-///
-public partial class GUITextBox : GUIComponent
-{
- private static bool initialized;
-
- public static GUIFrame IMEWindow { get; set; }
- public static GUITextBlock IMETextBlock { get; set; }
-
- public static void UpdateIME()
- {
- if (!initialized) { InitializeIME(); }
- if (GUI.KeyboardDispatcher.Subscriber is GUITextBox { Selected: true })
- {
- IMEWindow?.AddToGUIUpdateList(order: 10);
- }
- }
-
- private static void InitializeIME()
- {
- InputMethod.Initialize(GameMain.Instance.Window.Hwnd, false);
- InputMethod.TextCompositionCallback = OnTextComposition;
- InputMethod.CommitTextCompositionCallback = OnCommitTextComposition;
- InputMethod.Enabled = true;
- IMEWindow = new GUIFrame(new RectTransform(new Point(GUI.IntScale(300), GUI.IntScale(300)), GUI.Canvas), "InnerFrame") { CanBeFocused = false, Visible = false };
- IMETextBlock = new GUITextBlock(new RectTransform(Vector2.One, IMEWindow.RectTransform), "") { CanBeFocused = false };
-
- initialized = true;
- }
-
- private static void OnTextComposition(IMEString compositionText, int cursorPosition, IMEString[] candidateList, int candidatePageStart, int candidatePageSize, int candidateSelection)
- {
- if (GUI.KeyboardDispatcher.Subscriber is not GUITextBox { Selected: true } textBox) { return; }
- IMEWindow.Visible = true;
- string text = compositionText.ToString().Insert(cursorPosition, "|");
- if (candidateList != null)
- {
- text += "\n";
- for (int i = 0; i < candidatePageSize; i++)
- {
- string candidateStr = $"\t{candidatePageStart + i + 1} {candidateList[i]}";
- if (candidateSelection == i)
- {
- candidateStr = $" ‖color:{XMLExtensions.ToStringHex(Color.White)}‖{candidateStr}‖end‖";
- }
- candidateStr += "\n";
- text += candidateStr;
- }
- }
- IMETextBlock.Text = RichString.Rich(text);
-
- IMEWindow.RectTransform.NonScaledSize = new Point(
- Math.Max(IMEWindow.Rect.Width, (int)IMETextBlock.TextSize.X + GUI.IntScale(32)),
- (int)IMETextBlock.TextSize.Y);
-
- Point windowPos = new Point(textBox.Rect.X, textBox.Rect.Bottom);
- if (windowPos.Y + IMEWindow.Rect.Height > GameMain.GraphicsHeight)
- {
- windowPos.Y = textBox.Rect.Y - IMEWindow.Rect.Height;
- }
- IMEWindow.RectTransform.ScreenSpaceOffset = windowPos;
- }
-
- private static void OnCommitTextComposition(string text)
- {
- if (IMEWindow.Visible)
- {
- foreach (char c in text)
- {
- if (!char.IsControl(c))
- {
- GUI.KeyboardDispatcher.Subscriber?.ReceiveTextInput(c);
- }
- }
- }
- IMEWindow.Visible = false;
- }
-}
-
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/IMEPreviewText.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/IMEPreviewText.cs
index 96f137a08..3e57935de 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/IMEPreviewText.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/IMEPreviewText.cs
@@ -1,18 +1,25 @@
#nullable enable
+using System.Collections.Immutable;
using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
+
public sealed class IMEPreviewTextHandler
{
- public string PreviewText { get; private set; } = string.Empty;
- public Vector2 TextSize { get; private set; }
- public bool HasText => !string.IsNullOrEmpty(PreviewText);
+ public bool HasText => !string.IsNullOrEmpty(previewText);
// This has to be settable because for some reason we update the font of GUITextBox in some places
public GUIFont Font { get; set; }
+ private string previewText = string.Empty;
+ private Vector2 textSize;
+
+ private bool isSectioned;
+ private ImmutableArray? richTextData;
+
public IMEPreviewTextHandler(GUIFont font)
{
Font = font;
@@ -20,35 +27,64 @@ namespace Barotrauma
public void Reset()
{
- TextSize = Vector2.Zero;
- PreviewText = string.Empty;
+ textSize = Vector2.Zero;
+ previewText = string.Empty;
+ richTextData = null;
+ isSectioned = false;
}
- public void UpdateText(string text, int start)
+ public void UpdateText(string text, int start, int length)
{
- if (string.IsNullOrEmpty(text) && start is 0)
+ isSectioned = start >= 0 && length > 0;
+ richTextData = null;
+
+ if (string.IsNullOrEmpty(text))
{
Reset();
return;
}
- int totalLength = start + text.Length;
- string newText = PreviewText;
- if (newText.Length > totalLength)
- {
- newText = newText[..totalLength];
- }
+ previewText = text;
- if (totalLength > newText.Length)
- {
- // this is required for some reason on Windows
- // my guess is that the order which TextEditing events come thru is not guaranteed
- newText = newText.PadRight(totalLength);
- }
+ textSize = Font.MeasureString(text);
- newText = newText.Remove(start, text.Length).Insert(start, text);
- PreviewText = newText;
- TextSize = Font.MeasureString(PreviewText);
+ if (!isSectioned) { return; }
+
+ string coloredText = ToolBox.ColorSectionOfString(text, start, length, GUIStyle.Orange);
+
+ RichString richString = RichString.Rich(coloredText);
+
+ previewText = richString.SanitizedValue;
+ richTextData = richString.RichTextData;
+ }
+
+ public void DrawIMEPreview(SpriteBatch spriteBatch, Vector2 position, GUITextBlock textBlock)
+ {
+ if (!HasText) { return; }
+
+ int inflate = GUI.IntScale(3);
+
+ RectangleF rect = new RectangleF(position, textSize);
+ rect.Inflate(inflate, inflate);
+
+ RectangleF borderRect = rect;
+ borderRect.Inflate(1, 1);
+
+ GUI.DrawFilledRectangle(spriteBatch, borderRect, Color.White, depth: 0.02f);
+ GUI.DrawFilledRectangle(spriteBatch, rect, Color.Black, depth: 0.01f);
+
+ Font.DrawStringWithColors(spriteBatch,
+ text: previewText,
+ position: position,
+ color: isSectioned ? GUIStyle.TextColorNormal : GUIStyle.Orange,
+ rotation: 0.0f,
+ origin: Vector2.Zero,
+ scale: 1f,
+ spriteEffects: SpriteEffects.None,
+ layerDepth: 0,
+ richTextData: richTextData,
+ alignment: textBlock.TextAlignment,
+ forceUpperCase: textBlock.ForceUpperCase);
}
}
}
\ No newline at end of file
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs
index 3e9908ee4..1133e7e63 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs
@@ -377,6 +377,7 @@ namespace Barotrauma
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Hull));
performanceCounterTimer = Stopwatch.StartNew();
+ ResetIMEWorkaround();
}
///
@@ -1239,5 +1240,20 @@ namespace Barotrauma
};
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
+
+ /*
+ * On some systems, IME input is enabled by default, and being able to set the game to a state
+ * where it doesn't accept IME input on game launch seems very inconsistent.
+ * This function quickly cycles through IME input states and is called from couple different places
+ * to ensure that IME input is disabled properly when it's not needed.
+ */
+ public static void ResetIMEWorkaround()
+ {
+ Rectangle rect = new Rectangle(0, 0, GraphicsWidth, GraphicsHeight);
+ TextInput.SetTextInputRect(rect);
+ TextInput.StartTextInput();
+ TextInput.SetTextInputRect(rect);
+ TextInput.StopTextInput();
+ }
}
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs
index 2bd642ac6..0d77adef9 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs
@@ -999,6 +999,7 @@ namespace Barotrauma
foreach (MapEntityCategory category in Enum.GetValues(typeof(MapEntityCategory)))
{
+ if (category == MapEntityCategory.None) { continue; }
entityCategoryButtons.Add(new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), entityMenuTop.RectTransform, scaleBasis: ScaleBasis.BothHeight),
"", style: "CategoryButton." + category.ToString())
{
@@ -1085,6 +1086,7 @@ namespace Barotrauma
foreach (MapEntityCategory category in Enum.GetValues(typeof(MapEntityCategory)))
{
+ if (category == MapEntityCategory.None) { continue; }
LocalizedString categoryName = TextManager.Get("MapEntityCategory." + category);
maxTextWidth = (int)Math.Max(maxTextWidth, GUIStyle.SubHeadingFont.MeasureString(categoryName.Replace(" ", "\n")).X + GUI.IntScale(50));
foreach (MapEntityPrefab ep in MapEntityPrefab.List)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs
index 8eee8bff2..bb5f74966 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs
@@ -863,22 +863,21 @@ namespace Barotrauma
PlayDamageSound(damageType, damage, bodyPosition, 800.0f);
}
- private static readonly List tempList = new List();
public static void PlayDamageSound(string damageType, float damage, Vector2 position, float range = 2000.0f, IEnumerable tags = null)
{
- damage = MathHelper.Clamp(damage + Rand.Range(-10.0f, 10.0f), 0.0f, 100.0f);
- tempList.Clear();
- foreach (var s in damageSounds)
- {
- if ((s.DamageRange == Vector2.Zero ||
- (damage >= s.DamageRange.X && damage <= s.DamageRange.Y)) &&
- s.DamageType == damageType &&
- (s.RequiredTag.IsEmpty || (tags == null ? s.RequiredTag.IsEmpty : tags.Contains(s.RequiredTag))))
- {
- tempList.Add(s);
- }
- }
- var damageSound = tempList.GetRandomUnsynced();
+ //if the damage is too low for any sound, don't play anything
+ if (damageSounds.All(d => damage < d.DamageRange.X)) { return; }
+
+ //allow the damage to differ by 10 from the configured damage range,
+ //so the same amount of damage doesn't always play the same sound
+ float randomizedDamage = MathHelper.Clamp(damage + Rand.Range(-10.0f, 10.0f), 0.0f, 100.0f);
+
+ var suitableSounds = damageSounds.Where(s =>
+ s.DamageType == damageType &&
+ (s.DamageRange == Vector2.Zero || (randomizedDamage >= s.DamageRange.X && randomizedDamage <= s.DamageRange.Y)) &&
+ (s.RequiredTag.IsEmpty || (tags == null ? s.RequiredTag.IsEmpty : tags.Contains(s.RequiredTag))));
+
+ var damageSound = suitableSounds.GetRandomUnsynced();
damageSound?.Sound?.Play(1.0f, range, position, muffle: !damageSound.IgnoreMuffling && ShouldMuffleSound(Character.Controlled, position, range, null));
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Utils/ToolBox.cs b/Barotrauma/BarotraumaClient/ClientSource/Utils/ToolBox.cs
index 3d193471b..e04dfc070 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Utils/ToolBox.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Utils/ToolBox.cs
@@ -512,5 +512,34 @@ namespace Barotrauma
return new Vector2(paddingX, paddingY);
}
+
+ public static string ColorSectionOfString(string text, int start, int length, Color color)
+ {
+ int end = start + length;
+
+ if (start < 0 || length < 0 || end > text.Length)
+ {
+ throw new ArgumentOutOfRangeException($"Invalid start ({start}) or length ({length}) for text \"{text}\".");
+ }
+
+ string stichedString = string.Empty;
+
+ if (start > 0)
+ {
+ stichedString += text[..start];
+ }
+
+ // this is the highlighted part
+ stichedString += ColorString(text[start..end], color);
+
+ if (end < text.Length)
+ {
+ stichedString += text[end..];
+ }
+
+ return stichedString;
+
+ static string ColorString(string text, Color color) => $"‖color:{color.ToStringHex()}‖{text}‖end‖";
+ }
}
}
diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj
index d494aed4a..20fe2d70a 100644
--- a/Barotrauma/BarotraumaClient/LinuxClient.csproj
+++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma
- 0.20.9.0
+ 0.20.10.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
Barotrauma
diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj
index 0d49c9d86..120c891b2 100644
--- a/Barotrauma/BarotraumaClient/MacClient.csproj
+++ b/Barotrauma/BarotraumaClient/MacClient.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma
- 0.20.9.0
+ 0.20.10.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
Barotrauma
diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj
index eb0c23892..8b7efff85 100644
--- a/Barotrauma/BarotraumaClient/WindowsClient.csproj
+++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma
- 0.20.9.0
+ 0.20.10.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
Barotrauma
@@ -117,7 +117,6 @@
-
@@ -127,7 +126,6 @@
-
diff --git a/Barotrauma/BarotraumaClient/libSDL2-2.0.so.0 b/Barotrauma/BarotraumaClient/libSDL2-2.0.so.0
index bdd69273a..72cda07f7 100644
Binary files a/Barotrauma/BarotraumaClient/libSDL2-2.0.so.0 and b/Barotrauma/BarotraumaClient/libSDL2-2.0.so.0 differ
diff --git a/Barotrauma/BarotraumaClient/x64/SDL2.dll b/Barotrauma/BarotraumaClient/x64/SDL2.dll
index 6cf858caa..8c6230e6f 100644
Binary files a/Barotrauma/BarotraumaClient/x64/SDL2.dll and b/Barotrauma/BarotraumaClient/x64/SDL2.dll differ
diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj
index d4f7016e2..aaf80d9e3 100644
--- a/Barotrauma/BarotraumaServer/LinuxServer.csproj
+++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma Dedicated Server
- 0.20.9.0
+ 0.20.10.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
DedicatedServer
diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj
index ed6ba0f49..55f6e8b8c 100644
--- a/Barotrauma/BarotraumaServer/MacServer.csproj
+++ b/Barotrauma/BarotraumaServer/MacServer.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma Dedicated Server
- 0.20.9.0
+ 0.20.10.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
DedicatedServer
diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj
index 208a78b24..7c490e159 100644
--- a/Barotrauma/BarotraumaServer/WindowsServer.csproj
+++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma Dedicated Server
- 0.20.9.0
+ 0.20.10.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
DedicatedServer
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityCondition.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityCondition.cs
index 3ce84413a..ad4b4df25 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityCondition.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityCondition.cs
@@ -30,8 +30,7 @@ namespace Barotrauma.Abilities
NotSelf = 3,
Alive = 4,
Monster = 5,
- InFriendlySubmarine = 6,
- Large = 7,
+ InFriendlySubmarine = 6
};
protected List ParseTargetTypes(string[] targetTypeStrings)
@@ -80,9 +79,6 @@ namespace Barotrauma.Abilities
return !targetCharacter.IsHuman;
case TargetType.InFriendlySubmarine:
return targetCharacter.Submarine != null && targetCharacter.Submarine.TeamID == character.TeamID;
- case TargetType.Large:
- // mass of mudraptor is ~48
- return targetCharacter.AnimController is { Mass: > 50.0f };
default:
return true;
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionItem.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionItem.cs
index ae8597e64..993c19b94 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionItem.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionItem.cs
@@ -1,21 +1,25 @@
-using System;
+using Barotrauma.Extensions;
+using System;
+using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma.Abilities
{
class AbilityConditionItem : AbilityConditionData
{
- private readonly string[] identifiers;
- private readonly string[] tags;
+ private readonly ImmutableArray identifiers;
+ private readonly ImmutableArray tags;
+ private readonly MapEntityCategory category = MapEntityCategory.None;
public AbilityConditionItem(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
{
- identifiers = conditionElement.GetAttributeStringArray("identifiers", Array.Empty(), convertToLowerInvariant: true);
- tags = conditionElement.GetAttributeStringArray("tags", Array.Empty(), convertToLowerInvariant: true);
+ identifiers = conditionElement.GetAttributeIdentifierArray("identifiers", Array.Empty()).ToImmutableArray();
+ tags = conditionElement.GetAttributeIdentifierArray("tags", Array.Empty()).ToImmutableArray();
+ category = conditionElement.GetAttributeEnum("category", MapEntityCategory.None);
- if (!identifiers.Any() && !tags.Any())
+ if (identifiers.None() && tags.None() && category == MapEntityCategory.None)
{
- DebugConsole.ThrowError($"Error in talent \"{characterTalent}\". No identifiers or tags defined.");
+ DebugConsole.ThrowError($"Error in talent \"{characterTalent}\". No identifiers, tags or category defined.");
}
}
@@ -33,6 +37,11 @@ namespace Barotrauma.Abilities
if (itemPrefab != null)
{
+ if (category != MapEntityCategory.None)
+ {
+ if (!itemPrefab.Category.HasFlag(category)) { return false; }
+ }
+
if (identifiers.Any())
{
if (!identifiers.Any(t => itemPrefab.Identifier == t))
@@ -40,7 +49,6 @@ namespace Barotrauma.Abilities
return false;
}
}
-
return !tags.Any() || tags.Any(t => itemPrefab.Tags.Any(p => t == p));
}
else
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionDataless/AbilityConditionCrewMemberUnconscious.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionDataless/AbilityConditionCrewMemberUnconscious.cs
index 09fa7aaf4..ede7abe7a 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionDataless/AbilityConditionCrewMemberUnconscious.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionDataless/AbilityConditionCrewMemberUnconscious.cs
@@ -10,7 +10,7 @@ namespace Barotrauma.Abilities
{
foreach (Character c in GameSession.GetSessionCrewCharacters(CharacterType.Both))
{
- if (c.IsUnconscious)
+ if (!c.IsDead && c.IsUnconscious)
{
return true;
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbility.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbility.cs
index c4d73f406..5a7d22598 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbility.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbility.cs
@@ -34,7 +34,7 @@ namespace Barotrauma.Abilities
public bool IsViable()
{
- if (!AllowClientSimulation && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
+ if (!AllowClientSimulation && GameMain.NetworkMember is { IsClient: true }) { return false; }
if (RequiresAlive && Character.IsDead) { return false; }
return true;
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs
index b08d4163a..ca81f0634 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs
@@ -8,6 +8,8 @@ namespace Barotrauma.Abilities
{
internal sealed class CharacterAbilityUnlockApprenticeshipTalentTree : CharacterAbility
{
+ public override bool AllowClientSimulation => false;
+
public CharacterAbilityUnlockApprenticeshipTalentTree(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement) { }
public override void InitializeAbility(bool addingFirstTime)
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs
index 741afba2e..ad261f2c4 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs
@@ -417,14 +417,7 @@ namespace Barotrauma.Items.Components
item.body.FarseerBody.OnCollision += OnProjectileCollision;
item.body.FarseerBody.IsBullet = true;
-
- item.body.CollisionCategories = Physics.CollisionProjectile;
- item.body.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking;
- if (item.Prefab.DamagedByProjectiles && !IgnoreProjectilesWhileActive)
- {
- item.body.CollidesWith |= Physics.CollisionProjectile;
- }
-
+ EnableProjectileCollisions();
IsActive = true;
if (stickJoint == null) { return; }
@@ -549,11 +542,10 @@ namespace Barotrauma.Items.Components
}
else if (fixture?.Body == null || fixture.IsSensor)
{
- //ignore sensors and items
+ //ignore sensors
return true;
}
if (fixture.Body.UserData is VineTile) { return true; }
- if (fixture.Body.UserData is Item item && (item.GetComponent() == null && !item.Prefab.DamagedByProjectiles || item.Condition <= 0)) { return true; }
if (fixture.Body.UserData as string == "ruinroom" || fixture.Body.UserData is Hull || fixture.UserData is Hull) { return true; }
//if doing the raycast in a submarine's coordinate space, ignore anything that's not in that sub
@@ -563,13 +555,28 @@ namespace Barotrauma.Items.Components
if (fixture.Body.UserData is Entity entity && entity.Submarine != submarine) { return true; }
}
- //ignore everything else than characters, sub walls and level walls
- if (!fixture.CollisionCategories.HasFlag(Physics.CollisionCharacter) &&
- !fixture.CollisionCategories.HasFlag(Physics.CollisionWall) &&
- !fixture.CollisionCategories.HasFlag(Physics.CollisionLevel)) { return true; }
-
if (fixture.Body.UserData is VoronoiCell && (this.item.Submarine != null || submarine != null)) { return true; }
+ if (fixture.Body.UserData is Item item)
+ {
+ if (item == Item) { return true; }
+ if (item.Condition <= 0) { return true; }
+ if (!item.Prefab.DamagedByProjectiles && item.GetComponent() == null) { return true; }
+ }
+ else if (fixture.Body.UserData is Holdable { CanPush: false })
+ {
+ // Ignore holdables that can't push -> shouldn't block
+ return true;
+ }
+ else
+ {
+ // TODO: This might make us ignore something we don't want to ignore?
+ // Not item -> ignore everything else than characters, sub walls and level walls
+ if (!fixture.CollisionCategories.HasFlag(Physics.CollisionCharacter) &&
+ !fixture.CollisionCategories.HasFlag(Physics.CollisionWall) &&
+ !fixture.CollisionCategories.HasFlag(Physics.CollisionLevel)) { return true; }
+ }
+
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
@@ -586,20 +593,16 @@ namespace Barotrauma.Items.Components
}
else if (fixture?.Body == null || fixture.IsSensor)
{
- //ignore sensors and items
+ //ignore sensors
return -1;
}
if (fixture.Body.UserData is VineTile) { return -1; }
-
- if (fixture.Body.UserData is Item item && (item.GetComponent() == null && !item.Prefab.DamagedByProjectiles || item.Condition <= 0)) { return -1; }
- if (fixture.Body.UserData as string == "ruinroom" || fixture.Body?.UserData is Hull || fixture.UserData is Hull) { return -1; }
- if (!(fixture.Body.UserData is Holdable holdable && holdable.CanPush))
+ if (fixture.Body.UserData is Item item)
{
- //ignore everything else than characters, sub walls and level walls
- if (!fixture.CollisionCategories.HasFlag(Physics.CollisionCharacter) &&
- !fixture.CollisionCategories.HasFlag(Physics.CollisionWall) &&
- !fixture.CollisionCategories.HasFlag(Physics.CollisionLevel)) { return -1; }
+ if (item.Condition <= 0) { return -1; }
+ if (!item.Prefab.DamagedByProjectiles && item.GetComponent() == null) { return -1; }
}
+ if (fixture.Body.UserData as string == "ruinroom" || fixture.Body?.UserData is Hull || fixture.UserData is Hull) { return -1; }
//if doing the raycast in a submarine's coordinate space, ignore anything that's not in that sub
if (submarine != null)
@@ -609,6 +612,12 @@ namespace Barotrauma.Items.Components
if (fixture.Body.UserData is Limb limb && limb.character?.Submarine != submarine) { return -1; }
}
+ // Ignore holdables that can't push -> shouldn't block
+ if (fixture.Body.UserData is Holdable { CanPush: false })
+ {
+ return -1;
+ }
+
//ignore level cells if the item and the point of impact are inside a sub
if (fixture.Body.UserData is VoronoiCell)
{
@@ -639,7 +648,7 @@ namespace Barotrauma.Items.Components
hits.Add(new HitscanResult(fixture, point, normal, fraction));
return 1;
- }, rayStart, rayEnd, Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking);
+ }, rayStart, rayEnd, Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking | Physics.CollisionProjectile);
return hits;
}
@@ -759,6 +768,12 @@ namespace Barotrauma.Items.Components
else if (target.Body.UserData is Item item)
{
if (item.Condition <= 0.0f) { return false; }
+ if (!item.Prefab.DamagedByProjectiles) { return false; }
+ }
+ else if (target.Body.UserData is Holdable { CanPush: false })
+ {
+ // Ignore holdables that can't push -> shouldn't block
+ return false;
}
//ignore character colliders (the projectile only hits limbs)
@@ -774,7 +789,7 @@ namespace Barotrauma.Items.Components
{
item.body.FarseerBody.ResetDynamics();
}
- if (hits.Count() >= MaxTargetsToHit || target.Body.UserData is VoronoiCell)
+ if (hits.Count >= MaxTargetsToHit || target.Body.UserData is VoronoiCell)
{
DisableProjectileCollisions();
return true;
@@ -1047,6 +1062,16 @@ namespace Barotrauma.Items.Components
return true;
}
+ private void EnableProjectileCollisions()
+ {
+ item.body.CollisionCategories = Physics.CollisionProjectile;
+ item.body.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking;
+ if (!IgnoreProjectilesWhileActive)
+ {
+ item.body.CollidesWith |= Physics.CollisionProjectile;
+ }
+ }
+
private void DisableProjectileCollisions()
{
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs
index bdec8d1b4..6d9077cc0 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs
@@ -10,6 +10,7 @@ namespace Barotrauma
[Flags]
enum MapEntityCategory
{
+ None = 0,
Structure = 1,
Decorative = 2,
Machine = 4,
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Screens/Screen.cs b/Barotrauma/BarotraumaShared/SharedSource/Screens/Screen.cs
index 86cce0a43..622a47591 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Screens/Screen.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Screens/Screen.cs
@@ -35,6 +35,7 @@
{
GUI.DisableSavingIndicatorDelayed();
}
+ GameMain.ResetIMEWorkaround();
#endif
}
diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt
index c5f999537..959f6f31c 100644
--- a/Barotrauma/BarotraumaShared/changelog.txt
+++ b/Barotrauma/BarotraumaShared/changelog.txt
@@ -1,3 +1,28 @@
+---------------------------------------------------------------------------------------------------------
+v0.20.10.0
+---------------------------------------------------------------------------------------------------------
+
+Input Method Editor (Chinese/Japanese input) fixes:
+- Fixed inputs sometimes not working. Happened because listboxes stole keyboard focus, for example when rearranging bot orders or interacting with a chatbox.
+- Fixed moving the input position with arrow keys in the candidate box moving the caret in the textbox too.
+- Fixed current composition text disappearing when you browse through the options with arrow keys.
+- Fixed candidate box not being hidden when there's no input (= when you've just selected the textbox or committed the input).
+
+Fixes/improvements to depth charges:
+- Fixed projectile-on-projectile collisions not working properly.
+- Reduced the health of the depth charges (100 -> 10) to make them easier to detonate with turrets.
+- Made decoys self-destruct instead of just becoming disabled after 45 seconds, turning them into time bombs.
+- Fixed depth charges disappearing with a delay (after the explosion particles had gone away, making it easy to see them vanish).
+- Fixed regular depth charges being destructible before launching.
+
+Misc fixes:
+- Fixed "Graduation Ceremony" talent unlocking 2 talent trees instead of one.
+- Fixed Scrap Cannon's firing sound being barely audibly.
+- Fixed "Better Than New" allowing you to repair any device past the maximum condition, not just electrical devices.
+- Adjustments to Harpoon Coil-rifle sounds.
+- Fixed some sprite bleed issues in the new talent icons.
+- Fixed Kastrull's reactor having 1000 condition instead of 100.
+
---------------------------------------------------------------------------------------------------------
v0.20.9.0
---------------------------------------------------------------------------------------------------------
diff --git a/Libraries/ImeSharp/IMEString.cs b/Libraries/ImeSharp/IMEString.cs
deleted file mode 100644
index 6635c85ae..000000000
--- a/Libraries/ImeSharp/IMEString.cs
+++ /dev/null
@@ -1,201 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-
-namespace ImeSharp
-{
- public unsafe struct IMEString : IEnumerable
- {
- internal const int IMECharBufferSize = 64;
-
- public static readonly IMEString Empty = new IMEString((List)null);
-
- internal struct Enumerator : IEnumerator
- {
- private IMEString _imeString;
- private char _currentCharacter;
- private int _currentIndex;
-
- public Enumerator(IMEString imeString)
- {
- _imeString = imeString;
- _currentCharacter = '\0';
- _currentIndex = -1;
- }
-
- public bool MoveNext()
- {
- int size = _imeString.Count;
-
- _currentIndex++;
-
- if (_currentIndex == size)
- return false;
-
- fixed (char* ptr = _imeString.buffer)
- {
- _currentCharacter = *(ptr + _currentIndex);
- }
-
- return true;
- }
-
- public void Reset()
- {
- _currentIndex = -1;
- }
-
- public void Dispose()
- {
- }
-
- public char Current { get { return _currentCharacter; } }
- object IEnumerator.Current { get { return Current; } }
- }
-
- public int Count { get { return _size; } }
-
- public char this[int index]
- {
- get
- {
- if (index >= Count || index < 0)
- throw new ArgumentOutOfRangeException("index");
-
- fixed (char* ptr = buffer)
- {
- return *(ptr + index);
- }
- }
- }
-
- private int _size;
-
- fixed char buffer[IMECharBufferSize];
-
- public IMEString(string characters)
- {
- if (string.IsNullOrEmpty(characters))
- {
- _size = 0;
- return;
- }
-
- _size = characters.Length;
- if (_size > IMECharBufferSize)
- _size = IMECharBufferSize - 1;
-
- fixed (char* _ptr = buffer)
- {
- char* ptr = _ptr;
- for (var i = 0; i < _size; i++)
- {
- *ptr = characters[i];
- ptr++;
- }
- }
- }
-
- public IMEString(List characters)
- {
- if (characters == null || characters.Count == 0)
- {
- _size = 0;
- return;
- }
-
- _size = characters.Count;
- if (_size > IMECharBufferSize)
- _size = IMECharBufferSize - 1;
-
- fixed (char* _ptr = buffer)
- {
- char* ptr = _ptr;
- for (var i = 0; i < _size; i++)
- {
- *ptr = characters[i];
- ptr++;
- }
- }
- }
-
- public IMEString(char[] characters, int count)
- {
- if (characters == null || count <= 0)
- {
- _size = 0;
- return;
- }
-
- _size = count;
- if (_size > IMECharBufferSize)
- _size = IMECharBufferSize - 1;
-
- if (_size > characters.Length)
- _size = characters.Length;
-
- fixed (char* _ptr = buffer)
- {
- char* ptr = _ptr;
- for (var i = 0; i < _size; i++)
- {
- *ptr = characters[i];
- ptr++;
- }
- }
- }
-
- public IMEString(IntPtr bStrPtr)
- {
- if (bStrPtr == IntPtr.Zero)
- {
- _size = 0;
- return;
- }
-
- var ptrSrc = (char*)bStrPtr;
-
- int i = 0;
-
- fixed (char* _ptr = buffer)
- {
- char* ptr = _ptr;
-
- while (ptrSrc[i] != '\0')
- {
- *ptr = ptrSrc[i];
- i++;
- ptr++;
- }
- }
-
- _size = i;
- }
-
- public override string ToString()
- {
- fixed (char* ptr = buffer)
- {
- return new string(ptr, 0, _size);
- }
- }
-
- public IntPtr ToIntPtr()
- {
- fixed (char* ptr = buffer)
- {
- return (IntPtr)ptr;
- }
- }
-
- public IEnumerator GetEnumerator()
- {
- return new Enumerator(this);
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-}
diff --git a/Libraries/ImeSharp/IMETextCompositionEventArgs.cs b/Libraries/ImeSharp/IMETextCompositionEventArgs.cs
deleted file mode 100644
index 93548b0be..000000000
--- a/Libraries/ImeSharp/IMETextCompositionEventArgs.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-
-namespace ImeSharp
-{
- ///
- /// Arguments for the event.
- ///
- public struct IMETextCompositionEventArgs
- {
- ///
- // Construct a TextCompositionEventArgs with composition infos.
- ///
- public IMETextCompositionEventArgs(IMEString compositionText,
- int cursorPosition,
- IMEString[] candidateList = null,
- int candidatePageStart = 0,
- int candidatePageSize = 0,
- int candidateSelection = 0)
- {
- CompositionText = compositionText;
- CursorPosition = cursorPosition;
-
- CandidateList = candidateList;
- CandidatePageStart = candidatePageStart;
- CandidatePageSize = candidatePageSize;
- CandidateSelection = candidateSelection;
- }
-
- ///
- /// The full string as it's composed by the IMM.
- ///
- public readonly IMEString CompositionText;
-
- ///
- /// The position of the cursor inside the composed string.
- ///
- public readonly int CursorPosition;
-
- ///
- /// The candidate text list for the composition.
- /// This property is only supported on WindowsDX and WindowsUniversal.
- /// If the composition string does not generate candidates this array is empty.
- ///
- public readonly IMEString[] CandidateList;
-
- ///
- /// First candidate index of current page.
- ///
- public readonly int CandidatePageStart;
-
- ///
- /// How many candidates should display per page.
- ///
- public readonly int CandidatePageSize;
-
- ///
- /// The selected candidate index.
- ///
- public readonly int CandidateSelection;
- }
-}
diff --git a/Libraries/ImeSharp/IMETextInputEventArgs.cs b/Libraries/ImeSharp/IMETextInputEventArgs.cs
deleted file mode 100644
index 2d415a21e..000000000
--- a/Libraries/ImeSharp/IMETextInputEventArgs.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace ImeSharp
-{
- public struct IMETextInputEventArgs
- {
- public IMETextInputEventArgs(char character)
- {
- Character = character;
- }
-
- public readonly char Character;
- }
-}
\ No newline at end of file
diff --git a/Libraries/ImeSharp/ImeSharp.csproj b/Libraries/ImeSharp/ImeSharp.csproj
deleted file mode 100644
index 8cd198465..000000000
--- a/Libraries/ImeSharp/ImeSharp.csproj
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- net6.0
- true
-
-
-
- C# wrapper for Windows IME APIs. Its goal is to support both IMM32 and TSF.
- IME;netcoreapp3.1;net5.0;net6.0;winforms;windows;tsf;imm32
- ImeSharp
- https://github.com/ryancheung/ImeSharp
- https://github.com/ryancheung/ImeSharp
- ryancheung
- MIT
-
-
-
- ..\Artifacts
- ImeSharp
- ImeSharp
- 5
-
-
-
-
-
-
-
-
diff --git a/Libraries/ImeSharp/Imm32Manager.cs b/Libraries/ImeSharp/Imm32Manager.cs
deleted file mode 100644
index 9cf0c2768..000000000
--- a/Libraries/ImeSharp/Imm32Manager.cs
+++ /dev/null
@@ -1,349 +0,0 @@
-using System;
-using System.Buffers;
-using System.Runtime.InteropServices;
-using System.Globalization;
-using System.Diagnostics;
-using ImeSharp.Native;
-
-namespace ImeSharp
-{
- internal class Imm32Manager
- {
-
- // If the system is IMM enabled, this is true.
- private static bool _immEnabled = SafeSystemMetrics.IsImmEnabled;
-
- public static bool ImmEnabled { get { return _immEnabled; } }
-
- public const int LANG_CHINESE = 0x04;
- public const int LANG_KOREAN = 0x12;
- public const int LANG_JAPANESE = 0x11;
-
- public static int PRIMARYLANGID(int lgid)
- {
- return ((ushort)(lgid) & 0x3ff);
- }
-
- static Imm32Manager()
- {
- SetCurrentCulture();
- }
-
- ///
- /// return true if the current keyboard layout is a real IMM32-IME.
- ///
- public static bool IsImm32ImeCurrent()
- {
- if (!_immEnabled)
- return false;
-
- IntPtr hkl = NativeMethods.GetKeyboardLayout(0);
-
- return IsImm32Ime(hkl);
- }
-
- ///
- /// return true if the keyboard layout is a real IMM32-IME.
- ///
- public static bool IsImm32Ime(IntPtr hkl)
- {
- if (hkl == IntPtr.Zero)
- return false;
-
- return ((NativeMethods.IntPtrToInt32(hkl) & 0xf0000000) == 0xe0000000);
- }
-
- private static int _inputLanguageId;
-
- internal static void SetCurrentCulture()
- {
- var hkl = NativeMethods.GetKeyboardLayout(0);
- _inputLanguageId = NativeMethods.IntPtrToInt32(hkl) & 0xFFFF;
- }
-
- private IntPtr _windowHandle;
-
- private IntPtr _defaultImc;
- private IntPtr DefaultImc
- {
- get
- {
- if (_defaultImc == IntPtr.Zero)
- {
- IntPtr himc = NativeMethods.ImmCreateContext();
-
- // Store the default imc to _defaultImc.
- _defaultImc = himc;
- }
- return _defaultImc;
- }
- }
-
- private static ImmCompositionStringHandler _compositionStringHandler;
- private static ImmCompositionIntHandler _compositionCursorHandler;
-
- private bool _lastImmOpenStatus;
-
- public Imm32Manager(IntPtr windowHandle)
- {
- _windowHandle = windowHandle;
-
- _compositionStringHandler = new ImmCompositionStringHandler(DefaultImc, NativeMethods.GCS_COMPSTR);
- _compositionCursorHandler = new ImmCompositionIntHandler(DefaultImc, NativeMethods.GCS_CURSORPOS);
- }
-
- public static Imm32Manager Current
- {
- get
- {
- var defaultImm32Manager = InputMethod.DefaultImm32Manager;
-
- if (defaultImm32Manager == null)
- {
- defaultImm32Manager = new Imm32Manager(InputMethod.WindowHandle);
- InputMethod.DefaultImm32Manager = defaultImm32Manager;
- }
-
- return defaultImm32Manager;
- }
- }
-
- public void Enable()
- {
- if (DefaultImc != IntPtr.Zero)
- {
- // Create a temporary system caret
- NativeMethods.CreateCaret(_windowHandle, IntPtr.Zero, 2, 10);
- NativeMethods.ImmAssociateContext(_windowHandle, _defaultImc);
- }
- }
-
- public void Disable()
- {
- NativeMethods.ImmAssociateContext(_windowHandle, IntPtr.Zero);
- NativeMethods.DestroyCaret();
- }
-
- const int kCaretMargin = 1;
-
- // Set candidate window position.
- // Borrowed from https://github.com/chromium/chromium/blob/master/ui/base/ime/win/imm32_manager.cc
- public void SetCandidateWindow(TsfSharp.Rect caretRect)
- {
- int x = caretRect.Left;
- int y = caretRect.Top;
-
- if (PRIMARYLANGID(_inputLanguageId) == LANG_CHINESE)
- {
- // Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
- // when a user disables TSF (Text Service Framework) and CUAS (Cicero
- // Unaware Application Support).
- // On the other hand, when a user enables TSF and CUAS, Chinese IMEs
- // ignore the position of the current system caret and uses the
- // parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
- // parameter CFS_CANDIDATEPOS.
- // Therefore, we do not only call ::ImmSetCandidateWindow() but also
- // set the positions of the temporary system caret.
- var candidateForm = new NativeMethods.CANDIDATEFORM();
- candidateForm.dwStyle = NativeMethods.CFS_CANDIDATEPOS;
- candidateForm.ptCurrentPos.X = x;
- candidateForm.ptCurrentPos.Y = y;
- NativeMethods.ImmSetCandidateWindow(_defaultImc, ref candidateForm);
- }
-
- if (PRIMARYLANGID(_inputLanguageId) == LANG_JAPANESE)
- NativeMethods.SetCaretPos(x, caretRect.Bottom);
- else
- NativeMethods.SetCaretPos(x, y);
-
- // Set composition window position also to ensure move the candidate window position.
- var compositionForm = new NativeMethods.COMPOSITIONFORM();
- compositionForm.dwStyle = NativeMethods.CFS_POINT;
- compositionForm.ptCurrentPos.X = x;
- compositionForm.ptCurrentPos.Y = y;
- NativeMethods.ImmSetCompositionWindow(_defaultImc, ref compositionForm);
-
- if (PRIMARYLANGID(_inputLanguageId) == LANG_KOREAN)
- {
- // Chinese IMEs and Japanese IMEs require the upper-left corner of
- // the caret to move the position of their candidate windows.
- // On the other hand, Korean IMEs require the lower-left corner of the
- // caret to move their candidate windows.
- y += kCaretMargin;
- }
-
- // Need to return here since some Chinese IMEs would stuck if set
- // candidate window position with CFS_EXCLUDE style.
- if (PRIMARYLANGID(_inputLanguageId) == LANG_CHINESE) return;
-
- // Japanese IMEs and Korean IMEs also use the rectangle given to
- // ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
- // to move their candidate windows when a user disables TSF and CUAS.
- // Therefore, we also set this parameter here.
- var excludeRectangle = new NativeMethods.CANDIDATEFORM();
- compositionForm.dwStyle = NativeMethods.CFS_EXCLUDE;
- compositionForm.ptCurrentPos.X = x;
- compositionForm.ptCurrentPos.Y = y;
- compositionForm.rcArea.Left = x;
- compositionForm.rcArea.Top = y;
- compositionForm.rcArea.Right = caretRect.Right;
- compositionForm.rcArea.Bottom = caretRect.Bottom;
- NativeMethods.ImmSetCandidateWindow(_defaultImc, ref excludeRectangle);
- }
-
- internal bool ProcessMessage(IntPtr hWnd, uint msg, ref IntPtr wParam, ref IntPtr lParam)
- {
- switch (msg)
- {
- case NativeMethods.WM_INPUTLANGCHANGE:
- SetCurrentCulture();
- break;
- case NativeMethods.WM_IME_SETCONTEXT:
- if (wParam.ToInt32() == 1 && InputMethod.Enabled)
- {
- // Must re-associate ime context or things won't work.
- NativeMethods.ImmAssociateContext(_windowHandle, DefaultImc);
-
- if (_lastImmOpenStatus)
- NativeMethods.ImmSetOpenStatus(DefaultImc, true);
-
- var lParam64 = lParam.ToInt64();
- if (!InputMethod.ShowOSImeWindow)
- lParam64 &= ~NativeMethods.ISC_SHOWUICANDIDATEWINDOW;
- else
- lParam64 &= ~NativeMethods.ISC_SHOWUICOMPOSITIONWINDOW;
- lParam = (IntPtr)(int)lParam64;
- }
- break;
- case NativeMethods.WM_KILLFOCUS:
- _lastImmOpenStatus = NativeMethods.ImmGetOpenStatus(DefaultImc);
- break;
- case NativeMethods.WM_IME_NOTIFY:
- IMENotify(wParam.ToInt32());
- if (!InputMethod.ShowOSImeWindow)
- return true;
- break;
- case NativeMethods.WM_IME_STARTCOMPOSITION:
- //Debug.WriteLine("NativeMethods.WM_IME_STARTCOMPOSITION");
- IMEStartComposion(lParam.ToInt32());
- // Force to not show composition window, `lParam64 &= ~ISC_SHOWUICOMPOSITIONWINDOW` don't work sometime.
- return true;
- case NativeMethods.WM_IME_COMPOSITION:
- //Debug.WriteLine("NativeMethods.WM_IME_COMPOSITION");
- IMEComposition(lParam.ToInt32());
- break;
- case NativeMethods.WM_IME_ENDCOMPOSITION:
- //Debug.WriteLine("NativeMethods.WM_IME_ENDCOMPOSITION");
- IMEEndComposition(lParam.ToInt32());
- if (!InputMethod.ShowOSImeWindow)
- return true;
- break;
- }
-
- return false;
- }
-
- private void IMENotify(int WParam)
- {
- switch (WParam)
- {
- case NativeMethods.IMN_OPENCANDIDATE:
- case NativeMethods.IMN_CHANGECANDIDATE:
- IMEChangeCandidate();
- break;
- case NativeMethods.IMN_CLOSECANDIDATE:
- InputMethod.ClearCandidates();
- break;
- default:
- break;
- }
- }
-
- private void IMEChangeCandidate()
- {
- if (TextServicesLoader.ServicesInstalled) // TSF is enabled
- {
- if (!TextStore.Current.SupportUIElement) // But active IME not support UIElement
- UpdateCandidates(); // We have to fetch candidate list here.
-
- return;
- }
-
- // Normal candidate list fetch in IMM32
- UpdateCandidates();
- // Send event on candidate updates
- InputMethod.OnTextComposition(this, new IMEString(_compositionStringHandler.Values, _compositionStringHandler.Count), _compositionCursorHandler.Value);
-
- if (InputMethod.CandidateList != null)
- ArrayPool.Shared.Return(InputMethod.CandidateList);
- }
-
- private unsafe void UpdateCandidates()
- {
- uint length = NativeMethods.ImmGetCandidateList(DefaultImc, 0, IntPtr.Zero, 0);
- if (length > 0)
- {
- IntPtr pointer = Marshal.AllocHGlobal((int)length);
- length = NativeMethods.ImmGetCandidateList(DefaultImc, 0, pointer, length);
- NativeMethods.CANDIDATELIST* cList = (NativeMethods.CANDIDATELIST*)pointer;
-
- var selection = (int)cList->dwSelection;
- var pageStart = (int)cList->dwPageStart;
- var pageSize = (int)cList->dwPageSize;
-
- selection -= pageStart;
-
- IMEString[] candidates = ArrayPool.Shared.Rent(pageSize);
-
- int i, j;
- for (i = pageStart, j = 0; i < cList->dwCount && j < pageSize; i++, j++)
- {
- int sOffset = Marshal.ReadInt32(pointer, 24 + 4 * i);
- candidates[j] = new IMEString(pointer + sOffset);
- }
-
- //Debug.WriteLine("IMM========IMM");
- //Debug.WriteLine("pageStart: {0}, pageSize: {1}, selection: {2}, candidates:", pageStart, pageSize, selection);
- //for (int k = 0; k < candidates.Length; k++)
- // Debug.WriteLine(" {2}{0}.{1}", k + 1, candidates[k], k == selection ? "*" : "");
- //Debug.WriteLine("IMM++++++++IMM");
-
- InputMethod.CandidatePageStart = pageStart;
- InputMethod.CandidatePageSize = pageSize;
- InputMethod.CandidateSelection = selection;
- InputMethod.CandidateList = candidates;
-
- Marshal.FreeHGlobal(pointer);
- }
- }
-
- private void ClearComposition()
- {
- _compositionStringHandler.Clear();
- }
-
- private void IMEStartComposion(int lParam)
- {
- InputMethod.OnTextCompositionStarted(this);
- ClearComposition();
- }
-
- private void IMEComposition(int lParam)
- {
- if (_compositionStringHandler.Update(lParam))
- {
- _compositionCursorHandler.Update();
-
- InputMethod.OnTextComposition(this, new IMEString(_compositionStringHandler.Values, _compositionStringHandler.Count), _compositionCursorHandler.Value);
- }
- }
-
- private void IMEEndComposition(int lParam)
- {
- InputMethod.ClearCandidates();
- ClearComposition();
-
- InputMethod.OnTextCompositionEnded(this);
- }
- }
-}
diff --git a/Libraries/ImeSharp/ImmCompositionResultHandler.cs b/Libraries/ImeSharp/ImmCompositionResultHandler.cs
deleted file mode 100644
index 69d3d651b..000000000
--- a/Libraries/ImeSharp/ImmCompositionResultHandler.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using System;
-using System.Text;
-using System.Runtime.InteropServices;
-using ImeSharp.Native;
-
-namespace ImeSharp
-{
- internal abstract class ImmCompositionResultHandler
- {
- protected IntPtr _imeContext;
-
- public int Flag { get; private set; }
-
- internal ImmCompositionResultHandler(IntPtr imeContext, int flag)
- {
- this.Flag = flag;
- _imeContext = imeContext;
- }
-
- internal virtual void Update() { }
-
- internal bool Update(int lParam)
- {
- if ((lParam & Flag) == Flag)
- {
- Update();
- return true;
- }
- return false;
- }
- }
-
- internal class ImmCompositionStringHandler : ImmCompositionResultHandler
- {
- internal const int BufferSize = 1024;
- private byte[] _byteBuffer = new byte[BufferSize];
- private int _byteCount;
-
- private char[] _charBuffer = new char[BufferSize / 2];
- private int _charCount;
-
- public char[] Values { get { return _charBuffer; } }
- public int Count { get { return _charCount; } }
-
- public char this[int index]
- {
- get
- {
- if (index >= _charCount || index < 0)
- throw new ArgumentOutOfRangeException("index");
-
- return _charBuffer[index];
- }
- }
-
- internal ImmCompositionStringHandler(IntPtr imeContext, int flag) : base(imeContext, flag)
- {
- }
-
- public override string ToString()
- {
- if (_charCount <= 0)
- return string.Empty;
-
- return new string(_charBuffer, 0, _charCount);
- }
-
- internal void Clear()
- {
- Array.Clear(_byteBuffer, 0, _byteCount);
- _byteCount = 0;
-
- Array.Clear(_charBuffer, 0, _charCount);
- _charCount = 0;
- }
-
- internal override void Update()
- {
- _byteCount = NativeMethods.ImmGetCompositionString(_imeContext, Flag, IntPtr.Zero, 0);
- IntPtr pointer = Marshal.AllocHGlobal(_byteCount);
-
- try
- {
- Array.Clear(_byteBuffer, 0, _byteCount);
-
- if (_byteCount > 0)
- {
- NativeMethods.ImmGetCompositionString(_imeContext, Flag, pointer, _byteCount);
-
- Marshal.Copy(pointer, _byteBuffer, 0, _byteCount);
-
- Array.Clear(_charBuffer, 0, _charCount);
- _charCount = Encoding.Unicode.GetChars(_byteBuffer, 0, _byteCount, _charBuffer, 0);
- }
- }
- finally
- {
- Marshal.FreeHGlobal(pointer);
- }
- }
- }
-
- internal class ImmCompositionIntHandler : ImmCompositionResultHandler
- {
- public int Value { get; private set; }
-
- internal ImmCompositionIntHandler(IntPtr imeContext, int flag) : base(imeContext, flag) { }
-
- public override string ToString()
- {
- return Value.ToString();
- }
-
- internal override void Update()
- {
- Value = NativeMethods.ImmGetCompositionString(_imeContext, Flag, IntPtr.Zero, 0);
- }
- }
-}
diff --git a/Libraries/ImeSharp/InputMethod.cs b/Libraries/ImeSharp/InputMethod.cs
deleted file mode 100644
index fa178cbd7..000000000
--- a/Libraries/ImeSharp/InputMethod.cs
+++ /dev/null
@@ -1,246 +0,0 @@
-using System;
-using System.Globalization;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using ImeSharp.Native;
-
-namespace ImeSharp
-{
- public static class InputMethod
- {
- private static IntPtr _windowHandle;
- public static IntPtr WindowHandle { get { return _windowHandle; } }
-
- private static IntPtr _prevWndProc;
- private static NativeMethods.WndProcDelegate _wndProcDelegate;
-
- private static TextServicesContext _textServicesContext;
- internal static TextServicesContext TextServicesContext
- {
- get { return _textServicesContext; }
- set { _textServicesContext = value; }
- }
-
- private static TextStore _defaultTextStore;
- internal static TextStore DefaultTextStore
- {
- get { return _defaultTextStore; }
- set { _defaultTextStore = value; }
- }
-
- private static Imm32Manager _defaultImm32Manager;
- internal static Imm32Manager DefaultImm32Manager
- {
- get { return _defaultImm32Manager; }
- set { _defaultImm32Manager = value; }
- }
-
- private static bool _enabled;
- public static bool Enabled
- {
- get { return _enabled; }
- set
- {
- if (_enabled == value) return;
-
- _enabled = value;
-
- EnableOrDisableInputMethod(_enabled);
- }
- }
-
- internal static TsfSharp.Rect TextInputRect;
-
- ///
- /// Set the position of the candidate window rendered by the OS.
- /// Let the OS render the candidate window by set param "showOSImeWindow" to true on .
- ///
- public static void SetTextInputRect(int x, int y, int width, int height)
- {
- if (!_showOSImeWindow) return;
-
- TextInputRect.Left = x;
- TextInputRect.Top = y;
- TextInputRect.Right = x + width;
- TextInputRect.Bottom = y + height;
-
- if (Imm32Manager.ImmEnabled)
- Imm32Manager.Current.SetCandidateWindow(TextInputRect);
- }
-
- private static bool _showOSImeWindow = false;
-
- ///
- /// Return if let OS render IME Candidate window or not.
- ///
- public static bool ShowOSImeWindow { get { return _showOSImeWindow; } }
-
- internal static int CandidatePageStart;
- internal static int CandidatePageSize;
- internal static int CandidateSelection;
- internal static IMEString[] CandidateList;
-
- internal static void ClearCandidates()
- {
- CandidateList = null;
- CandidatePageStart = 0;
- CandidatePageSize = 0;
- CandidateSelection = 0;
- }
-
- public static event EventHandler TextComposition;
- public static event EventHandler TextInput;
- public static event EventHandler CommitTextComposition;
-
- public static TextInputCallback TextInputCallback { get; set; }
- public static TextCompositionCallback TextCompositionCallback { get; set; }
- public static CommitTextCompositionCallback CommitTextCompositionCallback { get; set; }
-
- ///
- /// Initialize InputMethod with a Window Handle.
- /// Let the OS render the candidate window by set to true.
- ///
- public static void Initialize(IntPtr windowHandle, bool showOSImeWindow = true)
- {
- if (_windowHandle != IntPtr.Zero)
- throw new InvalidOperationException("InputMethod can only be initialized once!");
-
- _windowHandle = windowHandle;
- _showOSImeWindow = showOSImeWindow;
-
- _wndProcDelegate = new NativeMethods.WndProcDelegate(WndProc);
- _prevWndProc = (IntPtr)NativeMethods.SetWindowLongPtr(_windowHandle, NativeMethods.GWL_WNDPROC,
- Marshal.GetFunctionPointerForDelegate(_wndProcDelegate));
- }
-
- internal static void OnTextInput(object sender, char character)
- {
- if (TextInput != null)
- TextInput.Invoke(sender, new IMETextInputEventArgs(character));
-
- if (TextInputCallback != null)
- TextInputCallback(character);
- }
-
- // Some Chinese IME only send composition start event but no composition update event.
- // We need this to ensure candidate window position can be set in time.
- internal static void OnTextCompositionStarted(object sender)
- {
- if (TextComposition != null)
- TextComposition.Invoke(sender, new IMETextCompositionEventArgs(IMEString.Empty, 0));
-
- if (TextCompositionCallback != null)
- TextCompositionCallback(IMEString.Empty, 0, null, 0, 0, 0);
- }
-
- // On text composition update.
- internal static void OnTextComposition(object sender, IMEString compositionText, int cursorPos)
- {
- if (compositionText.Count == 0) // Crash guard
- cursorPos = 0;
-
- if (cursorPos > compositionText.Count) // Another crash guard
- cursorPos = compositionText.Count;
-
- if (TextComposition != null)
- {
- TextComposition.Invoke(sender,
- new IMETextCompositionEventArgs(compositionText, cursorPos, CandidateList, CandidatePageStart, CandidatePageSize, CandidateSelection));
- }
-
- if (TextCompositionCallback != null)
- TextCompositionCallback(compositionText, cursorPos, CandidateList, CandidatePageStart, CandidatePageSize, CandidateSelection);
- }
-
- internal static void OnTextCompositionResult(object sender, string compositionResult)
- {
- if (CommitTextComposition != null)
- CommitTextComposition.Invoke(sender, compositionResult);
-
- if (CommitTextCompositionCallback != null)
- CommitTextCompositionCallback(compositionResult);
- }
-
- internal static void OnTextCompositionEnded(object sender)
- {
- if (TextComposition != null)
- TextComposition.Invoke(sender, new IMETextCompositionEventArgs(IMEString.Empty, 0));
-
- if (TextCompositionCallback != null)
- TextCompositionCallback(IMEString.Empty, 0, null, 0, 0, 0);
- }
-
- private static void EnableOrDisableInputMethod(bool bEnabled)
- {
- // InputMethod enable/disabled status was changed on the current focus Element.
- if (TextServicesLoader.ServicesInstalled)
- {
- if (bEnabled)
- TextServicesContext.Current.SetFocusOnDefaultTextStore();
- else
- TextServicesContext.Current.SetFocusOnEmptyDim();
- }
-
- // Under IMM32 enabled system, we associate default hIMC or null hIMC.
- if (Imm32Manager.ImmEnabled)
- {
- if (bEnabled)
- Imm32Manager.Current.Enable();
- else
- Imm32Manager.Current.Disable();
- }
- }
-
- private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
- {
- if (Imm32Manager.ImmEnabled)
- {
- if (Imm32Manager.Current.ProcessMessage(hWnd, msg, ref wParam, ref lParam))
- return IntPtr.Zero;
- }
-
- switch (msg)
- {
- case NativeMethods.WM_DESTROY:
- TextServicesContext.Current.Uninitialize(true);
- break;
- case NativeMethods.WM_CHAR:
- {
- if (InputMethod.Enabled)
- InputMethod.OnTextInput(null, (char)wParam.ToInt32());
-
- break;
- }
- }
-
- return NativeMethods.CallWindowProc(_prevWndProc, hWnd, msg, wParam, lParam);
- }
-
- ///
- /// Custom windows message pumping to fix frame stuck issue.
- /// Normally, you need call this method in handler.
- ///
- public static void PumpMessage()
- {
- if (!Enabled) return;
- if (!TextServicesLoader.ServicesInstalled) return;
-
- bool result;
- var msg = new NativeMethods.NativeMessage();
-
- do
- {
- result = NativeMethods.PeekMessage(out msg, _windowHandle, 0, 0, NativeMethods.PM_REMOVE);
-
- if (result)
- {
- NativeMethods.TranslateMessage(ref msg);
- NativeMethods.DispatchMessage(ref msg);
- }
- } while (result);
-
- NativeMethods.PostMessage(_windowHandle, NativeMethods.WM_NULL, IntPtr.Zero, IntPtr.Zero);
- }
- }
-}
diff --git a/Libraries/ImeSharp/Native/NativeMethods.cs b/Libraries/ImeSharp/Native/NativeMethods.cs
deleted file mode 100644
index 1e3d6f901..000000000
--- a/Libraries/ImeSharp/Native/NativeMethods.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using System;
-using System.Text;
-using System.Runtime.InteropServices;
-
-namespace ImeSharp.Native
-{
- public partial class NativeMethods
- {
- #region Constants
-
- public const int S_OK = 0x00000000;
- public const int S_FALSE = 0x00000001;
- public const int E_FAIL = unchecked((int)0x80004005);
- public const int E_INVALIDARG = unchecked((int)0x80070057);
- public const int E_NOTIMPL = unchecked((int)0x80004001);
-
- public const int WM_KEYFIRST = 0x0100;
- public const int WM_KEYDOWN = 0x0100;
- public const int WM_KEYUP = 0x0101;
- public const int WM_CHAR = 0x0102;
- public const int WM_DEADCHAR = 0x0103;
- public const int WM_SYSKEYDOWN = 0x0104;
- public const int WM_SYSKEYUP = 0x0105;
- public const int WM_SYSCHAR = 0x0106;
- public const int WM_SYSDEADCHAR = 0x0107;
- public const int WM_UNICHAR = 0x0109;
- public const int WM_KEYLAST = 0x0109;
- public const int UNICODE_NOCHAR = 0xFFFF;
-
- public const int WM_NOTIFY = 0x004E;
- public const int WM_INPUTLANGCHANGEREQUEST = 0x0050;
- public const int WM_INPUTLANGCHANGE = 0x0051;
- public const int WM_TCARD = 0x0052;
- public const int WM_HELP = 0x0053;
- public const int WM_USERCHANGED = 0x0054;
- public const int WM_NOTIFYFORMAT = 0x0055;
-
- public const int GWL_WNDPROC = -4;
-
- public const int WM_ACTIVATE = 0x0006;
- // WM_ACTIVATE state values
- public const int WA_INACTIVE = 0;
- public const int WA_ACTIVE = 1;
- public const int WA_CLICKACTIVE = 2;
-
- public const int WM_SETFOCUS = 0x0007;
- public const int WM_KILLFOCUS = 0x0008;
-
- public const int WM_DESTROY = 0x0002;
- public const int WM_NULL = 0x0000;
- public const int WM_QUIT = 0x0012;
-
- public const int CLSCTX_INPROC_SERVER = 0x1;
-
- public const int PM_NOREMOVE = 0x0000;
- public const int PM_REMOVE = 0x0001;
- public const int PM_NOYIELD = 0x0002;
-
- #endregion Constants
-
- #region Structs
-
- [StructLayout(LayoutKind.Sequential)]
- public struct NativeMessage
- {
- public IntPtr handle;
- public uint msg;
- public IntPtr wParam;
- public IntPtr lParam;
- public uint time;
- public int ptX;
- public int ptY;
- }
-
- #endregion
-
- [DllImport("user32.dll")]
- public static extern int GetSystemMetrics(SM nIndex);
-
- // We have this wrapper because casting IntPtr to int may
- // generate OverflowException when one of high 32 bits is set.
- public static int IntPtrToInt32(IntPtr intPtr)
- {
- return unchecked((int)intPtr.ToInt64());
- }
-
- [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
- public static extern IntPtr GetKeyboardLayout(int dwLayout);
-
- public delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
-
- [DllImport("user32.dll")]
- public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
-
- // This static method is required because legacy OSes do not support
- public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
- {
- if (IntPtr.Size == 8)
- return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
- else
- return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
- }
-
- [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Unicode)]
- private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);
-
- [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Unicode)]
- private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
-
- [DllImport("user32.dll", SetLastError = true)]
- public static extern bool GetWindowRect(IntPtr hwnd, out TsfSharp.Rect lpRect);
-
- [DllImport("user32", ExactSpelling = true, SetLastError = true)]
- public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, ref TsfSharp.Rect rect, [MarshalAs(UnmanagedType.U4)] int cPoints);
-
-
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, StringBuilder lParam);
-
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
-
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
-
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref IntPtr lParam);
-
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
-
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
-
- [DllImport("user32.dll")]
- public static extern bool TranslateMessage(ref NativeMessage lpMsg);
-
- [DllImport("user32.dll")]
- public static extern IntPtr DispatchMessage(ref NativeMessage lpmsg);
-
- [DllImport("User32.dll", CharSet = CharSet.Unicode)]
- public static extern bool PeekMessage(out NativeMessage msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
-
-
- [DllImport("ole32.dll", ExactSpelling = true, EntryPoint = "CoCreateInstance", PreserveSig = true)]
- public static extern int CoCreateInstance([In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, IntPtr pUnkOuter, int dwClsContext, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
-
- }
-}
diff --git a/Libraries/ImeSharp/Native/NativeMethodsIMM32.cs b/Libraries/ImeSharp/Native/NativeMethodsIMM32.cs
deleted file mode 100644
index 701d6aff2..000000000
--- a/Libraries/ImeSharp/Native/NativeMethodsIMM32.cs
+++ /dev/null
@@ -1,156 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace ImeSharp.Native
-{
- public partial class NativeMethods
- {
- #region Constants
-
- public const int WM_IME_SETCONTEXT = 0x0281;
- public const int WM_IME_NOTIFY = 0x0282;
- public const int WM_IME_CONTROL = 0x0283;
- public const int WM_IME_COMPOSITIONFULL = 0x0284;
- public const int WM_IME_SELECT = 0x0285;
- public const int WM_IME_CHAR = 0x0286;
- public const int WM_IME_REQUEST = 0x0288;
- public const int WM_IME_KEYDOWN = 0x0290;
- public const int WM_IME_KEYUP = 0x0291;
- public const int WM_IME_STARTCOMPOSITION = 0x010D;
- public const int WM_IME_ENDCOMPOSITION = 0x010E;
- public const int WM_IME_COMPOSITION = 0x010F;
- public const int WM_IME_KEYLAST = 0x010F;
-
- // wParam of report message WM_IME_NOTIFY
- public const int IMN_CLOSESTATUSWINDOW = 0x0001;
- public const int IMN_OPENSTATUSWINDOW = 0x0002;
- public const int IMN_CHANGECANDIDATE = 0x0003;
- public const int IMN_CLOSECANDIDATE = 0x0004;
- public const int IMN_OPENCANDIDATE = 0x0005;
- public const int IMN_SETCONVERSIONMODE = 0x0006;
- public const int IMN_SETSENTENCEMODE = 0x0007;
- public const int IMN_SETOPENSTATUS = 0x0008;
- public const int IMN_SETCANDIDATEPOS = 0x0009;
- public const int IMN_SETCOMPOSITIONFONT = 0x000A;
- public const int IMN_SETCOMPOSITIONWINDOW = 0x000B;
- public const int IMN_SETSTATUSWINDOWPOS = 0x000C;
- public const int IMN_GUIDELINE = 0x000D;
- public const int IMN_PRIVATE = 0x000E;
-
- // wParam of report message WM_IME_REQUEST
- public const int IMR_COMPOSITIONWINDOW = 0x0001;
- public const int IMR_CANDIDATEWINDOW = 0x0002;
- public const int IMR_COMPOSITIONFONT = 0x0003;
- public const int IMR_RECONVERTSTRING = 0x0004;
- public const int IMR_CONFIRMRECONVERTSTRING = 0x0005;
- public const int IMR_QUERYCHARPOSITION = 0x0006;
- public const int IMR_DOCUMENTFEED = 0x0007;
-
- // parameter of ImmGetCompositionString
- public const int GCS_COMPREADSTR = 0x0001;
- public const int GCS_COMPREADATTR = 0x0002;
- public const int GCS_COMPREADCLAUSE = 0x0004;
- public const int GCS_COMPSTR = 0x0008;
- public const int GCS_COMPATTR = 0x0010;
- public const int GCS_COMPCLAUSE = 0x0020;
- public const int GCS_CURSORPOS = 0x0080;
- public const int GCS_DELTASTART = 0x0100;
- public const int GCS_RESULTREADSTR = 0x0200;
- public const int GCS_RESULTREADCLAUSE = 0x0400;
- public const int GCS_RESULTSTR = 0x0800;
- public const int GCS_RESULTCLAUSE = 0x1000;
-
- public const int GCS_COMP = (GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE);
- public const int GCS_COMPREAD = (GCS_COMPREADSTR | GCS_COMPREADATTR | GCS_COMPREADCLAUSE);
- public const int GCS_RESULT = (GCS_RESULTSTR | GCS_RESULTCLAUSE);
- public const int GCS_RESULTREAD = (GCS_RESULTREADSTR | GCS_RESULTREADCLAUSE);
-
- public const int CFS_CANDIDATEPOS = 0x0040;
- public const int CFS_POINT = 0x0002;
- public const int CFS_EXCLUDE = 0x0080;
-
- // lParam for WM_IME_SETCONTEXT
- public const long ISC_SHOWUICANDIDATEWINDOW = 0x00000001;
- public const long ISC_SHOWUICOMPOSITIONWINDOW = 0x80000000;
- public const long ISC_SHOWUIGUIDELINE = 0x40000000;
- public const long ISC_SHOWUIALLCANDIDATEWINDOW = 0x0000000F;
- public const long ISC_SHOWUIALL = 0xC000000F;
-
- #endregion Constants
-
- [StructLayout(LayoutKind.Sequential)]
- public unsafe struct CANDIDATELIST
- {
- public uint dwSize;
- public uint dwStyle;
- public uint dwCount;
- public uint dwSelection;
- public uint dwPageStart;
- public uint dwPageSize;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.U4)]
- public fixed uint dwOffset[1];
- }
-
- // CANDIDATEFORM structures
- [StructLayout(LayoutKind.Sequential)]
- public struct CANDIDATEFORM
- {
- public int dwIndex;
- public int dwStyle;
- public TsfSharp.Point ptCurrentPos;
- public TsfSharp.Rect rcArea;
- }
-
- // COMPOSITIONFORM structures
- [StructLayout(LayoutKind.Sequential)]
- public struct COMPOSITIONFORM
- {
- public int dwStyle;
- public TsfSharp.Point ptCurrentPos;
- public TsfSharp.Rect rcArea;
- }
-
- [DllImport("imm32.dll", SetLastError = true)]
- public static extern IntPtr ImmCreateContext();
-
- [DllImport("imm32.dll", SetLastError = true)]
- public static extern bool ImmDestroyContext(IntPtr hIMC);
-
- [DllImport("imm32.dll", SetLastError = true)]
- public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
-
- [DllImport("imm32.dll", SetLastError = true)]
- public static extern IntPtr ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
-
- [DllImport("imm32.dll", CharSet = CharSet.Unicode)]
- public static extern uint ImmGetCandidateList(IntPtr hIMC, uint deIndex, IntPtr candidateList, uint dwBufLen);
-
- [DllImport("imm32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern int ImmGetCompositionString(IntPtr hIMC, int CompositionStringFlag, IntPtr buffer, int bufferLength);
-
- [DllImport("imm32.dll", SetLastError = true)]
- public static extern IntPtr ImmGetContext(IntPtr hWnd);
-
- [DllImport("Imm32.dll", SetLastError = true)]
- public static extern bool ImmGetOpenStatus(IntPtr hIMC);
-
- [DllImport("Imm32.dll", SetLastError = true)]
- public static extern bool ImmSetOpenStatus(IntPtr hIMC, bool open);
-
- [DllImport("imm32.dll", SetLastError = true)]
- public static extern bool ImmSetCandidateWindow(IntPtr hIMC, ref CANDIDATEFORM candidateForm);
-
- [DllImport("imm32.dll", SetLastError = true)]
- public static extern int ImmSetCompositionWindow(IntPtr hIMC, ref COMPOSITIONFORM compForm);
-
-
- [DllImport("user32.dll", SetLastError = true)]
- public static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight);
-
- [DllImport("user32.dll", SetLastError = true)]
- public static extern bool DestroyCaret();
-
- [DllImport("user32.dll", SetLastError = true)]
- public static extern bool SetCaretPos(int x, int y);
- }
-}
diff --git a/Libraries/ImeSharp/Native/NativeValues.cs b/Libraries/ImeSharp/Native/NativeValues.cs
deleted file mode 100644
index 1c0dd9a36..000000000
--- a/Libraries/ImeSharp/Native/NativeValues.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System;
-
-namespace ImeSharp.Native
-{
- ///
- /// SystemMetrics. SM_*
- ///
- public enum SM
- {
- CXSCREEN = 0,
- CYSCREEN = 1,
- CXVSCROLL = 2,
- CYHSCROLL = 3,
- CYCAPTION = 4,
- CXBORDER = 5,
- CYBORDER = 6,
- CXFIXEDFRAME = 7,
- CYFIXEDFRAME = 8,
- CYVTHUMB = 9,
- CXHTHUMB = 10,
- CXICON = 11,
- CYICON = 12,
- CXCURSOR = 13,
- CYCURSOR = 14,
- CYMENU = 15,
- CXFULLSCREEN = 16,
- CYFULLSCREEN = 17,
- CYKANJIWINDOW = 18,
- MOUSEPRESENT = 19,
- CYVSCROLL = 20,
- CXHSCROLL = 21,
- DEBUG = 22,
- SWAPBUTTON = 23,
- CXMIN = 28,
- CYMIN = 29,
- CXSIZE = 30,
- CYSIZE = 31,
- CXFRAME = 32,
- CXSIZEFRAME = CXFRAME,
- CYFRAME = 33,
- CYSIZEFRAME = CYFRAME,
- CXMINTRACK = 34,
- CYMINTRACK = 35,
- CXDOUBLECLK = 36,
- CYDOUBLECLK = 37,
- CXICONSPACING = 38,
- CYICONSPACING = 39,
- MENUDROPALIGNMENT = 40,
- PENWINDOWS = 41,
- DBCSENABLED = 42,
- CMOUSEBUTTONS = 43,
- SECURE = 44,
- CXEDGE = 45,
- CYEDGE = 46,
- CXMINSPACING = 47,
- CYMINSPACING = 48,
- CXSMICON = 49,
- CYSMICON = 50,
- CYSMCAPTION = 51,
- CXSMSIZE = 52,
- CYSMSIZE = 53,
- CXMENUSIZE = 54,
- CYMENUSIZE = 55,
- ARRANGE = 56,
- CXMINIMIZED = 57,
- CYMINIMIZED = 58,
- CXMAXTRACK = 59,
- CYMAXTRACK = 60,
- CXMAXIMIZED = 61,
- CYMAXIMIZED = 62,
- NETWORK = 63,
- CLEANBOOT = 67,
- CXDRAG = 68,
- CYDRAG = 69,
- SHOWSOUNDS = 70,
- CXMENUCHECK = 71,
- CYMENUCHECK = 72,
- SLOWMACHINE = 73,
- MIDEASTENABLED = 74,
- MOUSEWHEELPRESENT = 75,
- XVIRTUALSCREEN = 76,
- YVIRTUALSCREEN = 77,
- CXVIRTUALSCREEN = 78,
- CYVIRTUALSCREEN = 79,
- CMONITORS = 80,
- SAMEDISPLAYFORMAT = 81,
- IMMENABLED = 82,
- CXFOCUSBORDER = 83,
- CYFOCUSBORDER = 84,
- TABLETPC = 86,
- MEDIACENTER = 87,
- REMOTESESSION = 0x1000,
- REMOTECONTROL = 0x2001,
- }
-}
diff --git a/Libraries/ImeSharp/SafeSystemMetrics.cs b/Libraries/ImeSharp/SafeSystemMetrics.cs
deleted file mode 100644
index a2e76f5b2..000000000
--- a/Libraries/ImeSharp/SafeSystemMetrics.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using ImeSharp.Native;
-
-namespace ImeSharp
-{
- ///
- /// Contains properties that are queries into the system's various settings.
- ///
- internal sealed class SafeSystemMetrics
- {
-
- private SafeSystemMetrics()
- {
- }
-
- ///
- /// Maps to SM_CXDOUBLECLK
- ///
- public static int DoubleClickDeltaX
- {
- get { return NativeMethods.GetSystemMetrics(SM.CXDOUBLECLK); }
- }
-
- ///
- /// Maps to SM_CYDOUBLECLK
- ///
- public static int DoubleClickDeltaY
- {
- get { return NativeMethods.GetSystemMetrics(SM.CYDOUBLECLK); }
- }
-
-
- ///
- /// Maps to SM_CXDRAG
- ///
- public static int DragDeltaX
- {
- get { return NativeMethods.GetSystemMetrics(SM.CXDRAG); }
- }
-
- ///
- /// Maps to SM_CYDRAG
- ///
- public static int DragDeltaY
- {
- get { return NativeMethods.GetSystemMetrics(SM.CYDRAG); }
- }
-
- ///
- /// Is an IMM enabled ? Maps to SM_IMMENABLED
- ///
- public static bool IsImmEnabled
- {
- get { return (NativeMethods.GetSystemMetrics(SM.IMMENABLED) != 0); }
- }
-
- }
-}
diff --git a/Libraries/ImeSharp/TextInputCallbacks.cs b/Libraries/ImeSharp/TextInputCallbacks.cs
deleted file mode 100644
index c37837927..000000000
--- a/Libraries/ImeSharp/TextInputCallbacks.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace ImeSharp
-{
- public delegate void TextInputCallback(char character);
- public delegate void TextCompositionCallback(IMEString compositionText, int cursorPosition, IMEString[] candidateList, int candidatePageStart, int candidatePageSize, int candidateSelection);
- public delegate void CommitTextCompositionCallback(string text);
-}
diff --git a/Libraries/ImeSharp/TextServicesContext.cs b/Libraries/ImeSharp/TextServicesContext.cs
deleted file mode 100644
index 6ce846528..000000000
--- a/Libraries/ImeSharp/TextServicesContext.cs
+++ /dev/null
@@ -1,374 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Diagnostics;
-using ImeSharp.Native;
-using TsfSharp;
-
-namespace ImeSharp
-{
- //------------------------------------------------------
- //
- // TextServicesContext class
- //
- //------------------------------------------------------
-
- ///
- /// This class manages the ITfThreadMgr, EmptyDim and the reference to
- /// the default TextStore.
- ///
- ///
- ///
- internal class TextServicesContext
- {
- public const int TF_POPF_ALL = 0x0001;
- public const int TF_INVALID_COOKIE = -1;
- public static readonly Guid IID_ITfUIElementSink = new Guid(0xea1ea136, 0x19df, 0x11d7, 0xa6, 0xd2, 0x00, 0x06, 0x5b, 0x84, 0x43, 0x5c);
- public static readonly Guid IID_ITfTextEditSink = new Guid(0x8127d409, 0xccd3, 0x4683, 0x96, 0x7a, 0xb4, 0x3d, 0x5b, 0x48, 0x2b, 0xf7);
-
-
- public static TextServicesContext Current
- {
- get
- {
- if (InputMethod.TextServicesContext == null)
- InputMethod.TextServicesContext = new TextServicesContext();
-
- return InputMethod.TextServicesContext;
- }
- }
-
- //------------------------------------------------------
- //
- // Constructors
- //
- //------------------------------------------------------
-
- #region Constructors
-
- ///
- /// Instantiates a TextServicesContext.
- ///
- private TextServicesContext()
- {
- if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
- Debug.WriteLine("CRASH: ImeSharp won't work on MTA thread!!!");
- }
-
- #endregion Constructors
-
- //------------------------------------------------------
- //
- // public Methods
- //
- //------------------------------------------------------
-
- #region public Methods
-
- ///
- /// Releases all unmanaged resources allocated by the
- /// TextServicesContext.
- ///
- ///
- /// if appDomainShutdown == false, this method must be called on the
- /// Dispatcher thread. Otherwise, the caller is an AppDomain.Shutdown
- /// listener, and is calling from a worker thread.
- ///
- public void Uninitialize(bool appDomainShutdown)
- {
- // Unregister DefaultTextStore.
- if (_defaultTextStore != null)
- {
- UnadviseSinks();
- if (_defaultTextStore.DocumentManager != null)
- {
- _defaultTextStore.DocumentManager.Pop(TF_POPF_ALL);
- _defaultTextStore.DocumentManager.Dispose();
- _defaultTextStore.DocumentManager = null;
- }
-
- _defaultTextStore = null;
- }
-
- // Free up any remaining textstores.
- if (_istimactivated == true)
- {
- // Shut down the thread manager when the last TextStore goes away.
- // On XP, if we're called on a worker thread (during AppDomain shutdown)
- // we can't call call any methods on _threadManager. The problem is
- // that there's no proxy registered for ITfThreadMgr on OS versions
- // previous to Vista. Not calling Deactivate will leak the IMEs, but
- // in practice (1) they're singletons, so it's not unbounded; and (2)
- // most applications will share the thread with other AppDomains that
- // have a UI, in which case the IME won't be released until the process
- // shuts down in any case. In theory we could also work around this
- // problem by creating our own XP proxy/stub implementation, which would
- // be added to WPF setup....
- if (!appDomainShutdown || System.Environment.OSVersion.Version.Major >= 6)
- {
- _threadManager.Deactivate();
- }
- _istimactivated = false;
- }
-
- // Release the empty dim.
- if (_dimEmpty != null)
- {
- if (_dimEmpty != null)
- {
- _dimEmpty.Dispose();
- }
- _dimEmpty = null;
- }
-
- // Release the ThreadManager.
- // We don't do this in UnregisterTextStore because someone may have
- // called get_ThreadManager after the last TextStore was unregistered.
- if (_threadManager != null)
- {
- if (_threadManager != null)
- {
- _threadManager.Dispose();
- }
- _threadManager = null;
- }
- }
-
- // Called by framework's TextStore class. This method registers a
- // document with TSF. The TextServicesContext must maintain this list
- // to ensure all native resources are released after gc or uninitialization.
- public void RegisterTextStore(TextStore defaultTextStore)
- {
- _defaultTextStore = defaultTextStore;
-
- ITfThreadMgrEx threadManager = ThreadManager;
-
- if (threadManager != null)
- {
- ITfDocumentMgr doc;
- int editCookie = TF_INVALID_COOKIE;
-
- // Activate TSF on this thread if this is the first TextStore.
- if (_istimactivated == false)
- {
- //temp variable created to retrieve the value
- // which is then stored in the critical data.
- if (InputMethod.ShowOSImeWindow)
- _clientId = threadManager.Activate();
- else
- _clientId = threadManager.ActivateEx(TfTmaeFlags.Uielementenabledonly);
-
- _istimactivated = true;
- }
-
- // Create a TSF document.
- doc = threadManager.CreateDocumentMgr();
- _defaultTextStore.DocumentManager = doc;
-
- doc.CreateContext(_clientId, 0 /* flags */, _defaultTextStore, out _editContext, out editCookie);
- _defaultTextStore.EditCookie = editCookie;
- _contextOwnerServices = _editContext.QueryInterface();
-
- doc.Push(_editContext);
-
- AdviseSinks();
- }
- }
-
-
- public void SetFocusOnDefaultTextStore()
- {
- SetFocusOnDim(TextStore.Current.DocumentManager);
- }
-
- public void SetFocusOnEmptyDim()
- {
- SetFocusOnDim(EmptyDocumentManager);
- }
-
-
- #endregion public Methods
-
- //------------------------------------------------------
- //
- // public Properties
- //
- //------------------------------------------------------
-
- ///
- /// The default ITfThreadMgrEx object.
- ///
- public ITfThreadMgrEx ThreadManager
- {
- // The ITfThreadMgr for this thread.
- get
- {
- if (_threadManager == null)
- {
- ITfThreadMgr threadMgr = null;
- try
- {
- // This might fail in CoreRT
- threadMgr = Tsf.GetThreadMgr();
- }
- catch (SharpGen.Runtime.SharpGenException)
- {
- threadMgr = null;
- }
-
- // Dispose previous ITfThreadMgr in case something weird happens
- if (threadMgr != null)
- {
- if (threadMgr.IsThreadFocus)
- threadMgr.Deactivate();
- threadMgr.Dispose();
- }
-
- _threadManager = TextServicesLoader.Load();
-
- _uiElementMgr = _threadManager.QueryInterface();
- }
-
- return _threadManager;
- }
- }
-
- ///
- /// Return the created ITfContext object.
- ///
- public ITfContext EditContext
- {
- get { return _editContext; }
- }
-
- ///
- /// Return the created ITfUIElementMgr object.
- ///
- public ITfUIElementMgr UIElementMgr
- {
- get { return _uiElementMgr; }
- }
-
- ///
- /// Return the created ITfContextOwnerServices object.
- ///
- public ITfContextOwnerServices ContextOwnerServices
- {
- get { return _contextOwnerServices; }
- }
-
- //------------------------------------------------------
- //
- // public Events
- //
- //------------------------------------------------------
-
- //------------------------------------------------------
- //
- // Private Methods
- //
- //------------------------------------------------------
-
- private void SetFocusOnDim(ITfDocumentMgr dim)
- {
- ITfThreadMgrEx threadmgr = ThreadManager;
-
- if (threadmgr != null)
- {
- ITfDocumentMgr prevDocMgr = threadmgr.AssociateFocus(InputMethod.WindowHandle, dim);
- }
- }
-
- private void AdviseSinks()
- {
- var source = _uiElementMgr.QueryInterface();
- var guid = IID_ITfUIElementSink;
- int sinkCookie = source.AdviseSink(guid, _defaultTextStore);
- _defaultTextStore.UIElementSinkCookie = sinkCookie;
- source.Dispose();
-
- source = _editContext.QueryInterface();
- guid = IID_ITfTextEditSink;
- sinkCookie = source.AdviseSink(guid, _defaultTextStore);
- _defaultTextStore.TextEditSinkCookie = sinkCookie;
- source.Dispose();
- }
-
- private void UnadviseSinks()
- {
- var source = _uiElementMgr.QueryInterface();
-
- if (_defaultTextStore.UIElementSinkCookie != TF_INVALID_COOKIE)
- {
- source.UnadviseSink(_defaultTextStore.UIElementSinkCookie);
- _defaultTextStore.UIElementSinkCookie = TF_INVALID_COOKIE;
- }
- source.Dispose();
-
- source = _editContext.QueryInterface();
- if (_defaultTextStore.TextEditSinkCookie != TF_INVALID_COOKIE)
- {
- source.UnadviseSink(_defaultTextStore.TextEditSinkCookie);
- _defaultTextStore.TextEditSinkCookie = TF_INVALID_COOKIE;
- }
- source.Dispose();
- }
-
- //------------------------------------------------------
- //
- // Private Properties
- //
- //------------------------------------------------------
-
- // Create an empty dim on demand.
- private ITfDocumentMgr EmptyDocumentManager
- {
- get
- {
- if (_dimEmpty == null)
- {
- ITfThreadMgrEx threadManager = ThreadManager;
- if (threadManager == null)
- {
- return null;
- }
-
- ITfDocumentMgr dimEmptyTemp;
- // Create a TSF document.
- dimEmptyTemp = threadManager.CreateDocumentMgr();
- _dimEmpty = dimEmptyTemp;
- }
- return _dimEmpty;
- }
- }
-
-
- //------------------------------------------------------
- //
- // Private Fields
- //
- //------------------------------------------------------
-
- #region Private Fields
-
- private TextStore _defaultTextStore;
-
- private ITfContext _editContext;
- private ITfUIElementMgr _uiElementMgr;
- private ITfContextOwnerServices _contextOwnerServices;
-
- // This is true if thread manager is activated.
- private bool _istimactivated;
-
- // The root TSF object, created on demand.
- private ITfThreadMgrEx _threadManager;
-
- // TSF ClientId from Activate call.
- private int _clientId;
-
- // The empty dim for this thread. Created on demand.
- private ITfDocumentMgr _dimEmpty;
-
- #endregion Private Fields
- }
-}
diff --git a/Libraries/ImeSharp/TextServicesLoader.cs b/Libraries/ImeSharp/TextServicesLoader.cs
deleted file mode 100644
index ff2037b57..000000000
--- a/Libraries/ImeSharp/TextServicesLoader.cs
+++ /dev/null
@@ -1,338 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Diagnostics;
-using System.Threading;
-using Microsoft.Win32;
-using ImeSharp.Native;
-using TsfSharp;
-
-namespace ImeSharp
-{
- // Creates ITfThreadMgr instances, the root object of the Text Services
- // Framework.
- internal class TextServicesLoader
- {
- public static readonly Guid CLSID_TF_ThreadMgr = new Guid("529a9e6b-6587-4f23-ab9e-9c7d683e3c50");
- public static readonly Guid IID_ITfThreadMgr = new Guid("aa80e801-2021-11d2-93e0-0060b067b86e");
- public static readonly Guid IID_ITfThreadMgrEx = new Guid("3e90ade3-7594-4cb0-bb58-69628f5f458c");
- public static readonly Guid IID_ITfThreadMgr2 = new Guid("0AB198EF-6477-4EE8-8812-6780EDB82D5E");
-
- //------------------------------------------------------
- //
- // Constructors
- //
- //------------------------------------------------------
-
- #region Constructors
-
- // Private ctor to prevent anyone from instantiating this static class.
- private TextServicesLoader() { }
-
- #endregion Constructors
-
- //------------------------------------------------------
- //
- // public Properties
- //
- //------------------------------------------------------
-
- #region public Properties
-
- ///
- /// Loads an instance of the Text Services Framework.
- ///
- ///
- /// May return null if no text services are available.
- ///
- public static ITfThreadMgrEx Load()
- {
- if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
- Debug.WriteLine("CRASH: ImeSharp won't work on MTA thread!!!");
-
- if (ServicesInstalled)
- {
- // NB: a COMException here means something went wrong initialzing Cicero.
- // Cicero will throw an exception if it doesn't think it should have been
- // loaded (no TIPs to run), you can check that in msctf.dll's NoTipsInstalled
- // which lives in nt\windows\advcore\ctf\lib\immxutil.cpp. If that's the
- // problem, ServicesInstalled is out of sync with Cicero's thinking.
- IntPtr ret;
- var hr = NativeMethods.CoCreateInstance(CLSID_TF_ThreadMgr,
- IntPtr.Zero,
- NativeMethods.CLSCTX_INPROC_SERVER,
- IID_ITfThreadMgrEx, out ret);
-
- if (hr == NativeMethods.S_OK)
- return new ITfThreadMgrEx(ret);
- }
-
- return null;
- }
-
- ///
- /// return true if current OS version is Windows 7 or below.
- ///
- public static bool IsWindows7OrBelow()
- {
- if (Environment.OSVersion.Version.Major <= 5)
- return true;
-
- if (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor <= 1)
- return true;
-
- return false;
- }
-
- ///
- /// Informs the caller if text services are installed for the current user.
- ///
- ///
- /// true if one or more text services are installed for the current user, otherwise false.
- ///
- ///
- /// If this method returns false, TextServicesLoader.Load is guarenteed to return null.
- /// Callers can use this information to avoid overhead that would otherwise be
- /// required to support text services.
- ///
- public static bool ServicesInstalled
- {
- get
- {
- lock (s_servicesInstalledLock)
- {
- if (s_servicesInstalled == InstallState.Unknown)
- {
- s_servicesInstalled = TIPsWantToRun() ? InstallState.Installed : InstallState.NotInstalled;
- }
- }
-
- return (s_servicesInstalled == InstallState.Installed);
- }
- }
-
- #endregion public Properties
-
- //------------------------------------------------------
- //
- // public Events
- //
- //------------------------------------------------------
-
- //------------------------------------------------------
- //
- // Private Methods
- //
- //------------------------------------------------------
-
- #region Private Methods
-
- //
- // This method tries to stop Avalon from loading Cicero when there are no TIPs to run.
- // The perf tradeoff is a typically small number of registry checks versus loading and
- // initializing cicero.
- //
- // The Algorithm:
- //
- // Do a quick check vs. the global disable flag, return false if it is set.
- // For each key under HKLM\SOFTWARE\Microsoft\CTF\TIP (a TIP or category clsid)
- // If the the key has a LanguageProfile subkey (it's a TIP clsid)
- // Iterate under the matching TIP entry in HKCU.
- // For each key under the LanguageProfile (a particular LANGID)
- // For each key under the LANGID (an assembly GUID)
- // Try to read the Enable value.
- // If the value is set non-zero, then stop all processing and return true.
- // If the value is set zero, continue.
- // If the value does not exist, continue (default is disabled).
- // If any Enable values were found under HKCU for the TIP, then stop all processing and return false.
- // Else, no Enable values have been found thus far and we keep going to investigate HKLM.
- // Iterate under the TIP entry in HKLM.
- // For each key under the LanguageProfile (a particular LANGID)
- // For each key under the LANGID (an assembly GUID)
- // Try to read the Enable value.
- // If the value is set non-zero, then stop all processing and return true.
- // If the value does not exist, then stop all processing and return true (default is enabled).
- // If the value is set zero, continue.
- // If we finish iterating all entries under HKLM without returning true, return false.
- //
-
- private static bool TIPsWantToRun()
- {
- object obj;
- RegistryKey key;
- bool tipsWantToRun = false;
-
- key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\CTF", false);
-
- // Is cicero disabled completely for the current user?
- if (key != null)
- {
- obj = key.GetValue("Disable Thread Input Manager");
-
- if (obj is int && (int)obj != 0)
- return false;
- }
-
- // Loop through all the TIP entries for machine and current user.
- tipsWantToRun = IterateSubKeys(Registry.LocalMachine, "SOFTWARE\\Microsoft\\CTF\\TIP", new IterateHandler(SingleTIPWantsToRun), true) == EnableState.Enabled;
-
- return tipsWantToRun;
- }
-
- // Returns EnableState.Enabled if one or more TIPs are installed and
- // enabled for the current user.
- private static EnableState SingleTIPWantsToRun(RegistryKey keyLocalMachine, string subKeyName, bool localMachine)
- {
- EnableState result;
-
- if (subKeyName.Length != CLSIDLength)
- return EnableState.Disabled;
-
- // We want subkey\LanguageProfile key.
- // Loop through all the langid entries for TIP.
-
- // First, check current user.
- result = IterateSubKeys(Registry.CurrentUser, "SOFTWARE\\Microsoft\\CTF\\TIP\\" + subKeyName + "\\LanguageProfile", new IterateHandler(IsLangidEnabled), false);
-
- // Any explicit value short circuits the process.
- // Otherwise check local machine.
- if (result == EnableState.None || result == EnableState.Error)
- {
- result = IterateSubKeys(keyLocalMachine, subKeyName + "\\LanguageProfile", new IterateHandler(IsLangidEnabled), true);
-
- if (result == EnableState.None)
- {
- result = EnableState.Enabled;
- }
- }
-
- return result;
- }
-
- // Returns EnableState.Enabled if the supplied subkey is a valid LANGID key with enabled
- // cicero assembly.
- private static EnableState IsLangidEnabled(RegistryKey key, string subKeyName, bool localMachine)
- {
- if (subKeyName.Length != LANGIDLength)
- return EnableState.Error;
-
- // Loop through all the assembly entries for the langid
- return IterateSubKeys(key, subKeyName, new IterateHandler(IsAssemblyEnabled), localMachine);
- }
-
- // Returns EnableState.Enabled if the supplied assembly key is enabled.
- private static EnableState IsAssemblyEnabled(RegistryKey key, string subKeyName, bool localMachine)
- {
- RegistryKey subKey;
- object obj;
-
- if (subKeyName.Length != CLSIDLength)
- return EnableState.Error;
-
- // Open the local machine assembly key.
- subKey = key.OpenSubKey(subKeyName);
-
- if (subKey == null)
- return EnableState.Error;
-
- // Try to read the "Enable" value.
- obj = subKey.GetValue("Enable");
-
- if (obj is int)
- {
- return ((int)obj == 0) ? EnableState.Disabled : EnableState.Enabled;
- }
-
- return EnableState.None;
- }
-
- // Calls the supplied delegate on each of the children of keyBase.
- private static EnableState IterateSubKeys(RegistryKey keyBase, string subKey, IterateHandler handler, bool localMachine)
- {
- RegistryKey key;
- string[] subKeyNames;
- EnableState state;
-
- key = keyBase.OpenSubKey(subKey, false);
-
- if (key == null)
- return EnableState.Error;
-
- subKeyNames = key.GetSubKeyNames();
- state = EnableState.Error;
-
- foreach (string name in subKeyNames)
- {
- switch (handler(key, name, localMachine))
- {
- case EnableState.Error:
- break;
- case EnableState.None:
- if (localMachine) // For lm, want to return here right away.
- return EnableState.None;
-
- // For current user, remember that we found no Enable value.
- if (state == EnableState.Error)
- {
- state = EnableState.None;
- }
- break;
- case EnableState.Disabled:
- state = EnableState.Disabled;
- break;
- case EnableState.Enabled:
- return EnableState.Enabled;
- }
- }
-
- return state;
- }
-
- #endregion Private Methods
-
- //------------------------------------------------------
- //
- // Private Properties
- //
- //------------------------------------------------------
-
- //------------------------------------------------------
- //
- // Private Fields
- //
- //------------------------------------------------------
-
- #region Private Fields
-
- // String consts used to validate registry entires.
- private const int CLSIDLength = 38; // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
- private const int LANGIDLength = 10; // 0x12345678
-
- // Status of a TIP assembly.
- private enum EnableState
- {
- Error, // Invalid entry.
- None, // No explicit Enable entry on the assembly.
- Enabled, // Assembly is enabled.
- Disabled // Assembly is disabled.
- };
-
- // Callback delegate for the IterateSubKeys method.
- private delegate EnableState IterateHandler(RegistryKey key, string subKeyName, bool localMachine);
-
- // Install state.
- private enum InstallState
- {
- Unknown, // Haven't checked to see if any TIPs are installed yet.
- Installed, // Checked and installed.
- NotInstalled // Checked and not installed.
- }
-
- // Cached install state value.
- // Writes are not thread safe, but we don't mind the neglible perf hit
- // of potentially writing it twice.
- private static InstallState s_servicesInstalled = InstallState.Unknown;
- private static object s_servicesInstalledLock = new object();
-
- #endregion Private Fields
- }
-}
diff --git a/Libraries/ImeSharp/TextStore.cs b/Libraries/ImeSharp/TextStore.cs
deleted file mode 100644
index b7bf9cf87..000000000
--- a/Libraries/ImeSharp/TextStore.cs
+++ /dev/null
@@ -1,963 +0,0 @@
-using System;
-using System.Buffers;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using ImeSharp.Native;
-using SharpGen.Runtime;
-using SharpGen.Runtime.Win32;
-using TsfSharp;
-
-namespace ImeSharp
-{
- internal class TextStore : CallbackBase,
- ITextStoreACP,
- ITfContextOwnerCompositionSink,
- ITfTextEditSink,
- ITfUIElementSink
- {
- public static readonly Guid IID_ITextStoreACPSink = new Guid(0x22d44c94, 0xa419, 0x4542, 0xa2, 0x72, 0xae, 0x26, 0x09, 0x3e, 0xce, 0xcf);
- public static readonly Guid GUID_PROP_COMPOSING = new Guid("e12ac060-af15-11d2-afc5-00105a2799b5");
-
- //------------------------------------------------------
- //
- // Constructors
- //
- //------------------------------------------------------
-
- #region Constructors
-
- // Creates a TextStore instance.
- public TextStore(IntPtr windowHandle)
- {
- _windowHandle = windowHandle;
-
-
- _viewCookie = Environment.TickCount;
-
- _editCookie = Tsf.TF_INVALID_COOKIE;
- _uiElementSinkCookie = Tsf.TF_INVALID_COOKIE;
- _textEditSinkCookie = Tsf.TF_INVALID_COOKIE;
-
- _IMEStringPool = ArrayPool.Shared;
- }
-
- #endregion Constructors
-
- //------------------------------------------------------
- //
- // Methods - ITextStoreACP
- //
- //------------------------------------------------------
-
- #region ITextStoreACP
-
- public void AdviseSink(Guid riid, IUnknown obj, int flags)
- {
- ITextStoreACPSink sink;
-
- if (riid != IID_ITextStoreACPSink)
- throw new COMException("TextStore_CONNECT_E_CANNOTCONNECT");
-
- sink = (obj as ComObject).QueryInterface();
-
- if (sink == null)
- throw new COMException("TextStore_E_NOINTERFACE");
-
- // It's legal to replace existing sink.
- if (_sink != null)
- _sink.Dispose();
-
- (obj as ComObject).Dispose();
-
- _sink = sink;
- }
-
- public void UnadviseSink(IUnknown obj)
- {
- var sink = (obj as ComObject).QueryInterface();
- if (sink.NativePointer != _sink.NativePointer)
- throw new COMException("TextStore_CONNECT_E_NOCONNECTION");
-
- _sink.Release();
- _sink = null;
- }
-
- private bool _LockDocument(TsfSharp.TsLfFlags dwLockFlags)
- {
- if (_locked)
- return false;
-
- _locked = true;
- _lockFlags = dwLockFlags;
-
- return true;
- }
-
- private void ResetIfRequired()
- {
- if (!_commited)
- return;
-
- _commited = false;
-
- TsTextchange textChange;
- textChange.AcpStart = 0;
- textChange.AcpOldEnd = _inputBuffer.Count;
- textChange.AcpNewEnd = 0;
- _inputBuffer.Clear();
-
- _sink.OnTextChange(0, textChange);
-
- _acpStart = _acpEnd = 0;
- _sink.OnSelectionChange();
- _commitStart = _commitLength = 0;
-
- //Debug.WriteLine("TextStore reset!!!");
- }
-
- private void _UnlockDocument()
- {
- Result hr;
- _locked = false;
- _lockFlags = 0;
-
- ResetIfRequired();
-
- //if there is a queued lock, grant it
- if (_lockRequestQueue.Count > 0)
- {
- hr = RequestLock(_lockRequestQueue.Dequeue());
- }
-
- //if any layout changes occurred during the lock, notify the manager
- if (_layoutChanged)
- {
- _layoutChanged = false;
- _sink.OnLayoutChange(TsLayoutCode.TsLcChange, _viewCookie);
- }
- }
-
- private bool _IsLocked(TsfSharp.TsLfFlags dwLockType)
- {
- return _locked && (_lockFlags & dwLockType) != 0;
- }
-
- public Result RequestLock(TsfSharp.TsLfFlags dwLockFlags)
- {
- Result hrSession;
-
- if (_sink == null)
- throw new COMException("TextStore_NoSink");
-
- if (dwLockFlags == 0)
- throw new COMException("TextStore_BadLockFlags");
-
- hrSession = Result.Fail;
-
- if (_locked)
- {
- //the document is locked
-
- if ((dwLockFlags & TsfSharp.TsLfFlags.Sync) == TsfSharp.TsLfFlags.Sync)
- {
- /*
- The caller wants an immediate lock, but this cannot be granted because
- the document is already locked.
- */
- hrSession = (int)TsErrors.TsESynchronous;
- }
- else
- {
- //the request is asynchronous
-
- //Queue the lock request
- _lockRequestQueue.Enqueue(dwLockFlags);
- hrSession = (int)TsErrors.TsSAsync;
- }
-
- return hrSession;
- }
-
- //lock the document
- _LockDocument(dwLockFlags);
-
- //call OnLockGranted
- hrSession = _sink.OnLockGranted(dwLockFlags);
-
- //unlock the document
- _UnlockDocument();
-
- return hrSession;
- }
-
- public TsStatus GetStatus()
- {
- TsStatus status = new TsStatus();
- status.DynamicFlags = 0;
- status.StaticFlags = 0;
-
- return status;
- }
-
- public void QueryInsert(int acpTestStart, int acpTestEnd, uint cch, out int acpResultStart, out int acpResultEnd)
- {
- acpResultStart = acpResultEnd = 0;
-
- // Fix possible crash
- if (_inputBuffer.Count == 0)
- return;
-
- //Queryins
- if (acpTestStart > _inputBuffer.Count || acpTestEnd > _inputBuffer.Count)
- throw new COMException("", Result.InvalidArg.Code);
-
- //Microsoft Pinyin seems does not init the result value, so we set the test value here, in case crash
- acpResultStart = acpTestStart;
- acpResultEnd = acpTestEnd;
- }
-
- public uint GetSelection(uint index, ref TsSelectionAcp selection)
- {
- //does the caller have a lock
- if (!_IsLocked(TsLfFlags.Read))
- {
- //the caller doesn't have a lock
- //return NativeMethods.TS_E_NOLOCK;
- throw new COMException("", (int)TsErrors.TsENolock);
- }
-
- //check the requested index
- if (-1 == (int)index)
- {
- index = 0;
- }
- else if (index > 1)
- {
- /*
- The index is too high. This app only supports one selection.
- */
- throw new COMException("", Result.InvalidArg.Code);
- }
-
- selection.AcpStart = _acpStart;
- selection.AcpEnd = _acpEnd;
- selection.Style.InterimCharFlag = _interimChar;
-
- if (_interimChar)
- {
- /*
- fInterimChar will be set when an intermediate character has been
- set. One example of when this will happen is when an IME is being
- used to enter characters and a character has been set, but the IME
- is still active.
- */
- selection.Style.Ase = TsActiveSelEnd.TsAeNone;
- }
- else
- {
- selection.Style.Ase = _activeSelectionEnd;
- }
-
- return 1;
- }
-
- public void SetSelection(uint count, ref TsSelectionAcp selections)
- {
- //this implementaiton only supports a single selection
- if (count != 1)
- throw new COMException("", Result.InvalidArg.Code);
-
- //does the caller have a lock
- if (!_IsLocked(TsLfFlags.Readwrite))
- {
- //the caller doesn't have a lock
- //return NativeMethods.TS_E_NOLOCK;
- throw new COMException("", (int)TsErrors.TsENolock);
- }
-
- _acpStart = selections.AcpStart;
- _acpEnd = selections.AcpEnd;
- _interimChar = selections.Style.InterimCharFlag;
-
- if (_interimChar)
- {
- /*
- fInterimChar will be set when an intermediate character has been
- set. One example of when this will happen is when an IME is being
- used to enter characters and a character has been set, but the IME
- is still active.
- */
- _activeSelectionEnd = TsActiveSelEnd.TsAeNone;
- }
- else
- {
- _activeSelectionEnd = selections.Style.Ase;
- }
-
- //if the selection end is at the start of the selection, reverse the parameters
- int lStart = _acpStart;
- int lEnd = _acpEnd;
-
- if (TsActiveSelEnd.TsAeStart == _activeSelectionEnd)
- {
- lStart = _acpEnd;
- lEnd = _acpStart;
- }
- }
-
-
- public void GetText(int acpStart, int acpEnd, System.IntPtr pchPlain, uint cchPlainReq, out uint cchPlainRet,
- ref TsfSharp.TsRuninfo rgRunInfo, uint cRunInfoReq, out uint cRunInfoRet, out int acpNext)
- {
- cchPlainRet = 0;
- cRunInfoRet = 0;
- acpNext = 0;
-
- //does the caller have a lock
- if (!_IsLocked(TsLfFlags.Read))
- {
- //the caller doesn't have a lock
- throw new COMException("", (int)TsErrors.TsENolock);
- }
-
- bool fDoText = cchPlainReq > 0;
- bool fDoRunInfo = cRunInfoReq > 0;
- int cchTotal;
-
- cchPlainRet = 0;
- acpNext = acpStart;
-
- cchTotal = _inputBuffer.Count;
-
- //validate the start pos
- if ((acpStart < 0) || (acpStart > cchTotal))
- {
- throw new COMException("", Result.InvalidArg.Code);
- }
- else
- {
- //are we at the end of the document
- if (acpStart == cchTotal)
- {
- return;
- }
- else
- {
- int cchReq;
-
- /*
- acpEnd will be -1 if all of the text up to the end is being requested.
- */
-
- if (acpEnd >= acpStart)
- {
- cchReq = acpEnd - acpStart;
- }
- else
- {
- cchReq = cchTotal - acpStart;
- }
-
- if (fDoText)
- {
- if (cchReq > cchPlainReq)
- {
- cchReq = (int)cchPlainReq;
- }
-
- //extract the specified text range
- if (pchPlain != IntPtr.Zero && cchPlainReq > 0)
- {
- //_inputBuffer.CopyTo(acpStart, pchPlain, 0, cchReq);
-
- unsafe
- {
- var ptr = (char*)pchPlain;
-
- for (int i = acpStart; i < cchReq; i++)
- {
- *ptr = _inputBuffer[i];
- ptr++;
- }
- }
- }
- }
-
- //it is possible that only the length of the text is being requested
- cchPlainRet = (uint)cchReq;
-
- if (fDoRunInfo)
- {
- /*
- Runs are used to separate text characters from formatting characters.
-
- In this example, sequences inside and including the <> are treated as
- control sequences and are not displayed.
-
- Plain text = "Text formatting."
- Actual text = "Text formatting."
-
- If all of this text were requested, the run sequence would look like this:
-
- prgRunInfo[0].type = TS_RT_PLAIN; //"Text "
- prgRunInfo[0].uCount = 5;
-
- prgRunInfo[1].type = TS_RT_HIDDEN; //
- prgRunInfo[1].uCount = 6;
-
- prgRunInfo[2].type = TS_RT_PLAIN; //"formatting"
- prgRunInfo[2].uCount = 10;
-
- prgRunInfo[3].type = TS_RT_HIDDEN; //
- prgRunInfo[3].uCount = 8;
-
- prgRunInfo[4].type = TS_RT_PLAIN; //"."
- prgRunInfo[4].uCount = 1;
-
- TS_RT_OPAQUE is used to indicate characters or character sequences
- that are in the document, but are used privately by the application
- and do not map to text. Runs of text tagged with TS_RT_OPAQUE should
- NOT be included in the pchPlain or cchPlainOut [out] parameters.
- */
-
- /*
- This implementation is plain text, so the text only consists of one run.
- If there were multiple runs, it would be an error to have consecuative runs
- of the same type.
- */
- rgRunInfo.Type = TsRunType.TsRtPlain;
- rgRunInfo.Count = (uint)cchReq;
- }
-
- acpNext = acpStart + cchReq;
- }
- }
- }
-
- public TsTextchange SetText(int dwFlags, int acpStart, int acpEnd, string pchText, uint cch)
- {
- /*
- dwFlags can be:
- TS_ST_CORRECTION
- */
- TsTextchange change = new TsTextchange();
-
- //set the selection to the specified range
- TsSelectionAcp tsa = new TsSelectionAcp();
- tsa.AcpStart = acpStart;
- tsa.AcpEnd = acpEnd;
- tsa.Style.Ase = TsActiveSelEnd.TsAeStart;
- tsa.Style.InterimCharFlag = false;
-
- SetSelection(1, ref tsa);
-
- int start, end;
- InsertTextAtSelection(TsIasFlags.Noquery, pchText, cch, out start, out end, out change);
-
- return change;
- }
-
- public IDataObject GetFormattedText(int startIndex, int endIndex)
- {
- throw new COMException("", Result.NotImplemented.Code);
- }
-
- public IUnknown GetEmbedded(int index, Guid guidService, Guid riid)
- {
- throw new COMException("", Result.NotImplemented.Code);
- }
-
- public RawBool QueryInsertEmbedded(Guid guidService, ref Formatetc formatEtc)
- {
- throw new COMException("", Result.NotImplemented.Code);
- }
-
- public TsTextchange InsertEmbedded(int flags, int startIndex, int endIndex, TsfSharp.IDataObject dataObjectRef)
- {
- throw new COMException("", Result.NotImplemented.Code);
- }
-
- public void InsertTextAtSelection(TsfSharp.TsIasFlags dwFlags, string pchText, uint cch, out int pacpStart, out int pacpEnd, out TsfSharp.TsTextchange pChange)
- {
- pacpStart = pacpEnd = 0;
- pChange = new TsTextchange();
-
- //does the caller have a lock
- if (!_IsLocked(TsLfFlags.Readwrite))
- {
- //the caller doesn't have a lock
- throw new COMException("", (int)TsErrors.TsENolock);
- }
-
- int acpStart;
- int acpOldEnd;
- int acpNewEnd;
-
- acpOldEnd = _acpEnd;
-
- //set the start point after the insertion
- acpStart = _acpStart;
-
- //set the end point after the insertion
- acpNewEnd = _acpStart + (int)cch;
-
- if ((dwFlags & TsIasFlags.Queryonly) == TsIasFlags.Queryonly)
- {
- pacpStart = acpStart;
- pacpEnd = acpOldEnd;
- return;
- }
-
- //insert the text
- _inputBuffer.RemoveRange(acpStart, acpOldEnd - acpStart);
- _inputBuffer.InsertRange(acpStart, pchText);
-
- //set the selection
- _acpStart = acpStart;
- _acpEnd = acpNewEnd;
-
- if ((dwFlags & TsIasFlags.Noquery) != TsIasFlags.Noquery)
- {
- pacpStart = acpStart;
- pacpEnd = acpNewEnd;
- }
-
- //set the TS_TEXTCHANGE members
- pChange.AcpStart = acpStart;
- pChange.AcpOldEnd = acpOldEnd;
- pChange.AcpNewEnd = acpNewEnd;
-
- //defer the layout change notification until the document is unlocked
- _layoutChanged = true;
- }
-
- public void InsertEmbeddedAtSelection(int flags, IDataObject obj, out int startIndex, out int endIndex, out TsTextchange change)
- {
- startIndex = endIndex = 0;
- change = new TsTextchange();
- throw new COMException("", Result.NotImplemented.Code);
- }
-
- public void RequestSupportedAttrs(int flags, uint cFilterAttrs, ref Guid filterAttributes)
- {
- }
-
- public void RequestAttrsAtPosition(int index, uint cFilterAttrs, ref Guid filterAttributes, int flags)
- {
- throw new COMException("", Result.NotImplemented.Code);
- }
-
-
- public void RequestAttrsTransitioningAtPosition(int position, uint cFilterAttrs, ref Guid filterAttributes, int flags)
- {
- throw new COMException("", Result.NotImplemented.Code);
- }
-
- public void FindNextAttrTransition(int startIndex, int haltIndex, uint cFilterAttrs, ref Guid filterAttributes, int flags, out int acpNext, out RawBool found, out int foundOffset)
- {
- acpNext = 0;
- found = false;
- foundOffset = 0;
- }
-
- public uint RetrieveRequestedAttrs(uint ulCount, ref TsfSharp.TsAttrval aAttrValsRef)
- {
- return 0;
- }
-
- public int GetEndACP()
- {
- int acp = 0;
- //does the caller have a lock
- if (!_IsLocked(TsLfFlags.Read))
- {
- //the caller doesn't have a lock
- throw new COMException("", (int)TsErrors.TsENolock);
- }
-
- acp = _inputBuffer.Count;
-
- return acp;
- }
-
- public int GetActiveView()
- {
- return _viewCookie;
- }
-
- public int GetACPFromPoint(int viewCookie, TsfSharp.Point tsfPoint, int dwFlags)
- {
- throw new COMException("", Result.NotImplemented.Code);
- }
-
- public void GetTextExt(int viewCookie, int acpStart, int acpEnd, out Rect rect, out RawBool clipped)
- {
- clipped = false;
- rect = InputMethod.TextInputRect;
-
- if (_viewCookie != viewCookie)
- throw new COMException("", Result.InvalidArg.Code);
-
- //does the caller have a lock
- if (!_IsLocked(TsLfFlags.Read))
- {
- //the caller doesn't have a lock
- throw new COMException("", (int)TsErrors.TsENolock);
- }
-
- //According to Microsoft's doc, an ime should not make empty request,
- //but some ime draw comp text themseleves, when empty req will be make
- //Check empty request
- //if (acpStart == acpEnd) {
- // return E_INVALIDARG;
- //}
-
- NativeMethods.MapWindowPoints(_windowHandle, IntPtr.Zero, ref rect, 2);
- }
-
- public Rect GetScreenExt(int viewCookie)
- {
- Rect rect = new Rect();
-
- if (_viewCookie != viewCookie)
- throw new COMException("", Result.InvalidArg.Code);
-
- NativeMethods.GetWindowRect(_windowHandle, out rect);
-
- return rect;
- }
-
- public IntPtr GetWnd(int viewCookie)
- {
- if (viewCookie != _viewCookie)
- {
- throw new COMException("", Result.False.Code);
- }
-
- return _windowHandle;
- }
-
- #endregion ITextStoreACP2
-
-
- //------------------------------------------------------
- //
- // Public Methods - ITfContextOwnerCompositionSink
- //
- //------------------------------------------------------
-
- #region ITfContextOwnerCompositionSink
-
- public RawBool OnStartComposition(ITfCompositionView view)
- {
- // Return true in ok to start the composition.
- RawBool ok = true;
- _compositionStart = _compositionLength = 0;
- _currentComposition.Clear();
-
- InputMethod.OnTextCompositionStarted(this);
- _compViews.Add(view);
-
- return ok;
- }
-
- public void OnUpdateComposition(ITfCompositionView view, ITfRange rangeNew)
- {
- var range = view.Range;
- var rangeacp = range.QueryInterface();
-
- rangeacp.GetExtent(out _compositionStart, out _compositionLength);
- rangeacp.Dispose();
- range.Dispose();
- _compViews.Add(view);
- }
-
- public void OnEndComposition(ITfCompositionView view)
- {
- var range = view.Range;
- var rangeacp = range.QueryInterface();
-
- rangeacp.GetExtent(out _commitStart, out _commitLength);
- rangeacp.Dispose();
- range.Dispose();
-
- // Ensure composition string reset
- _compositionStart = _compositionLength = 0;
- _currentComposition.Clear();
-
- InputMethod.ClearCandidates();
- InputMethod.OnTextCompositionEnded(this);
- view.Dispose();
- foreach(var item in _compViews)
- item.Dispose();
- _compViews.Clear();
- }
-
- #endregion ITfContextOwnerCompositionSink
-
- #region ITfTextEditSink
-
- public void OnEndEdit(ITfContext context, int ecReadOnly, ITfEditRecord editRecord)
- {
- ITfProperty property = context.GetProperty(GUID_PROP_COMPOSING);
-
- ITfRangeACP rangeACP = TextServicesContext.Current.ContextOwnerServices.CreateRange(_compositionStart, _compositionStart + _compositionLength);
- Variant val = property.GetValue(ecReadOnly, rangeACP);
- property.Dispose();
- rangeACP.Dispose();
- if (val.Value == null || (int)val.Value == 0)
- {
- if (_commitLength == 0 || _inputBuffer.Count == 0)
- return;
-
- //Debug.WriteLine("Composition result: {0}", new object[] { new string(_inputBuffer.GetRange(_commitStart, _commitLength).ToArray()) });
-
- _commited = true;
- for (int i = 0; i < _commitLength; i++)
- InputMethod.OnTextCompositionResult(this, new string(_inputBuffer.GetRange(_commitStart, _commitLength).ToArray()));
- }
-
- if (_commited)
- return;
-
- if (_inputBuffer.Count == 0 && _compositionLength > 0) // Composition just ended
- return;
-
- _currentComposition.Clear();
- for (int i = 0; i < _compositionLength; i++)
- _currentComposition.Add(_inputBuffer[_compositionStart + i]);
-
- InputMethod.OnTextComposition(this, new IMEString(_currentComposition), _acpEnd);
-
- //var compStr = new string(_currentComposition.ToArray());
- //compStr = compStr.Insert(_acpEnd, "|");
- //Debug.WriteLine("Composition string: {0}, cursor pos: {1}", compStr, _acpEnd);
- }
-
- #endregion ITfTextEditSink
-
- //------------------------------------------------------
- //
- // Public Methods - ITfUIElementSink
- //
- //------------------------------------------------------
-
- #region ITfUIElementSink
-
- public RawBool BeginUIElement(int dwUIElementId)
- {
- // Hide OS rendered Candidate list Window
- RawBool pbShow = InputMethod.ShowOSImeWindow;
-
- OnUIElement(dwUIElementId, true);
-
- return pbShow;
- }
-
- public void UpdateUIElement(int dwUIElementId)
- {
- OnUIElement(dwUIElementId, false);
- }
-
- public void EndUIElement(int dwUIElementId)
- {
- }
-
- private void OnUIElement(int uiElementId, bool onStart)
- {
- if (InputMethod.ShowOSImeWindow || !_supportUIElement) return;
-
- ITfUIElement uiElement = TextServicesContext.Current.UIElementMgr.GetUIElement(uiElementId);
-
- ITfCandidateListUIElementBehavior candList;
-
- try
- {
- candList = uiElement.QueryInterface();
- }
- catch (SharpGenException)
- {
- _supportUIElement = false;
- return;
- }
- finally
- {
- uiElement.Dispose();
- }
-
- uint selection = 0;
- uint currentPage = 0;
- uint count = 0;
- uint pageCount = 0;
- uint pageStart = 0;
- uint pageSize = 0;
- uint i, j;
-
- selection = candList.GetSelection();
- currentPage = candList.GetCurrentPage();
-
- count = candList.GetCount();
-
- pageCount = candList.GetPageIndex(null, 0);
-
- if (pageCount > 0)
- {
- uint[] pageStartIndexes = ArrayPool.Shared.Rent((int)pageCount);
- pageCount = candList.GetPageIndex(pageStartIndexes, pageCount);
- pageStart = pageStartIndexes[currentPage];
-
- if (pageStart >= count - 1)
- {
- candList.Abort();
- ArrayPool.Shared.Return(pageStartIndexes);
- return;
- }
-
- if (currentPage < pageCount - 1)
- pageSize = Math.Min(count, pageStartIndexes[currentPage + 1]) - pageStart;
- else
- pageSize = count - pageStart;
-
- ArrayPool.Shared.Return(pageStartIndexes);
- }
-
- selection -= pageStart;
-
- IMEString[] candidates = _IMEStringPool.Rent((int)pageSize);
-
- IntPtr bStrPtr;
- for (i = pageStart, j = 0; i < count && j < pageSize; i++, j++)
- {
- bStrPtr = candList.GetString(i);
- candidates[j] = new IMEString(bStrPtr);
- }
-
- //Debug.WriteLine("TSF========TSF");
- //Debug.WriteLine("pageStart: {0}, pageSize: {1}, selection: {2}, currentPage: {3} candidates:", pageStart, pageSize, selection, currentPage);
- //for (int k = 0; k < candidates.Length; k++)
- // Debug.WriteLine(" {2}{0}.{1}", k + 1, candidates[k], k == selection ? "*" : "");
- //Debug.WriteLine("TSF++++++++TSF");
-
- InputMethod.CandidatePageStart = (int)pageStart;
- InputMethod.CandidatePageSize = (int)pageSize;
- InputMethod.CandidateSelection = (int)selection;
- InputMethod.CandidateList = candidates;
-
- if (_currentComposition != null)
- {
- InputMethod.OnTextComposition(this, new IMEString(_currentComposition), _acpEnd);
- _IMEStringPool.Return(candidates);
- }
-
- candList.Dispose();
- }
-
- #endregion ITfUIElementSink
-
- //------------------------------------------------------
- //
- // Public Properties
- //
- //------------------------------------------------------
-
- public static TextStore Current
- {
- get
- {
- TextStore defaultTextStore = InputMethod.DefaultTextStore;
- if (defaultTextStore == null)
- {
- defaultTextStore = InputMethod.DefaultTextStore = new TextStore(InputMethod.WindowHandle);
-
- defaultTextStore.Register();
- }
-
- return defaultTextStore;
- }
- }
-
- public ITfDocumentMgr DocumentManager
- {
- get { return _documentMgr; }
- set { _documentMgr = value; }
- }
-
- // EditCookie for ITfContext.
- public int EditCookie
- {
- // get { return _editCookie; }
- set { _editCookie = value; }
- }
-
- public int UIElementSinkCookie
- {
- get { return _uiElementSinkCookie; }
- set { _uiElementSinkCookie = value; }
- }
-
- public int TextEditSinkCookie
- {
- get { return _textEditSinkCookie; }
- set { _textEditSinkCookie = value; }
- }
-
- public bool SupportUIElement { get { return _supportUIElement; } }
-
-
- //------------------------------------------------------
- //
- // Private Methods
- //
- //------------------------------------------------------
-
- // This function calls TextServicesContext to create TSF document and start transitory extension.
- private void Register()
- {
- // Create TSF document and advise the sink to it.
- TextServicesContext.Current.RegisterTextStore(this);
- }
-
- //------------------------------------------------------
- //
- // Private Fields
- //
- //------------------------------------------------------
-
- // The TSF document object. This is a native resource.
- private ITfDocumentMgr _documentMgr;
-
- private int _viewCookie;
-
- // The edit cookie TSF returns from CreateContext.
- private int _editCookie;
- private int _uiElementSinkCookie;
- private int _textEditSinkCookie;
-
- private ITextStoreACPSink _sink;
- private IntPtr _windowHandle;
- private int _acpStart;
- private int _acpEnd;
- private bool _interimChar;
- private TsActiveSelEnd _activeSelectionEnd;
- private List _inputBuffer = new List();
-
- private bool _locked;
- private TsLfFlags _lockFlags;
- private Queue _lockRequestQueue = new Queue();
- private bool _layoutChanged;
-
- private List _currentComposition = new List();
- private int _compositionStart;
- private int _compositionLength;
- private int _commitStart;
- private int _commitLength;
- private bool _commited;
-
- private bool _supportUIElement = true;
- private List _compViews = new List();
-
- private ArrayPool _IMEStringPool;
-
- }
-}
diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/GameWindow.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/GameWindow.cs
index 8b166be16..3e73f5980 100644
--- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/GameWindow.cs
+++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/GameWindow.cs
@@ -111,6 +111,11 @@ namespace Microsoft.Xna.Framework {
/// Used for displaying uncommitted IME text.
///
public event EventHandler TextEditing;
+
+ ///
+ /// Used for when a key is pressed, including modifiers.
+ ///
+ public event EventHandler KeyDown;
#endif
#endregion Events
@@ -158,6 +163,11 @@ namespace Microsoft.Xna.Framework {
EventHelpers.Raise(this, TextInput, e);
}
+ protected void OnKeyDown(object sender, TextInputEventArgs e)
+ {
+ EventHelpers.Raise(this, KeyDown, e);
+ }
+
protected void OnTextEditing(object sender, TextEditingEventArgs e)
{
EventHelpers.Raise(this, TextEditing, e);
diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDL2.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDL2.cs
index 8b4672ef6..43f2d7f32 100644
--- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDL2.cs
+++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDL2.cs
@@ -75,6 +75,8 @@ internal static class Sdl
TextEditing = 0x302,
TextInput = 0x303,
+ TextEditingExt = 0x305,
+
MouseMotion = 0x400,
MouseButtonDown = 0x401,
MouseButtonup = 0x402,
@@ -139,6 +141,8 @@ internal static class Sdl
[FieldOffset(0)]
public Keyboard.TextEditingEvent Edit;
[FieldOffset(0)]
+ public Keyboard.TextEditingExtEvent EditExt;
+ [FieldOffset(0)]
public Keyboard.TextInputEvent Text;
[FieldOffset(0)]
public Mouse.WheelEvent Wheel;
@@ -241,8 +245,8 @@ internal static class Sdl
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate bool d_sdl_istextinputactive();
- public static d_sdl_istextinputactive SDL_IsTextInputActive = FuncLoader.LoadFunction(NativeLibrary, "SDL_IsTextInputActive");
+ public delegate bool d_sdl_istextinputshown();
+ public static d_sdl_istextinputshown SDL_IsTextInputShown = FuncLoader.LoadFunction(NativeLibrary, "SDL_IsTextInputShown");
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void d_sdl_starttextinput();
@@ -841,6 +845,17 @@ internal static class Sdl
public int Length;
}
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct TextEditingExtEvent
+ {
+ public EventType Type;
+ public uint Timestamp;
+ public uint WindowId;
+ public byte* Text;
+ public int Start;
+ public int Length;
+ }
+
[StructLayout(LayoutKind.Sequential)]
public unsafe struct TextInputEvent
{
diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDLGamePlatform.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDLGamePlatform.cs
index fb2f05833..6f3a4f18b 100644
--- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDLGamePlatform.cs
+++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDLGamePlatform.cs
@@ -147,14 +147,7 @@ namespace Microsoft.Xna.Framework
character = '\0';
}
- if (char.IsControl(character) ||
- key == Keys.Left ||
- key == Keys.Right ||
- key == Keys.Up ||
- key == Keys.Down)
- {
- _view.CallTextInput(character, key);
- }
+ _view.CallKeyDown(character, key);
}
else if (ev.Type == Sdl.EventType.KeyUp)
{
@@ -164,10 +157,24 @@ namespace Microsoft.Xna.Framework
else if (ev.Type == Sdl.EventType.TextEditing)
{
string text;
- unsafe { text = ReadString(ev.Text.Text); }
+ unsafe
+ {
+ text = ReadString(ev.Edit.Text);
+ }
_view.CallTextEditing(text, ev.Edit.Start, ev.Edit.Length);
}
+ else if (ev.Type == Sdl.EventType.TextEditingExt)
+ {
+ string text;
+ unsafe
+ {
+ text = ReadString(ev.EditExt.Text);
+ Sdl.Free((IntPtr)ev.EditExt.Text);
+ }
+
+ _view.CallTextEditing(text, ev.EditExt.Start, ev.EditExt.Length);
+ }
else if (ev.Type == Sdl.EventType.TextInput)
{
string text;
diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDLGameWindow.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDLGameWindow.cs
index 91eab7e6a..5da9dc065 100644
--- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDLGameWindow.cs
+++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/SDL/SDLGameWindow.cs
@@ -117,7 +117,8 @@ namespace Microsoft.Xna.Framework
* By default SDL2 will hide IME popups since it probably assumes the game will implement their own suggestions box.
* We don't want that, so this hint will allow the system native IME popups to show up when typing in the game.
*/
- Sdl.SetHint("SDL_HINT_IME_SHOW_UI", "1");
+ Sdl.SetHint("SDL_IME_SHOW_UI", "1");
+ Sdl.SetHint("SDL_IME_SUPPORT_EXTENDED_TEXT", "1");
// when running NUnit tests entry assembly can be null
if (Assembly.GetEntryAssembly() != null)
@@ -339,6 +340,11 @@ namespace Microsoft.Xna.Framework
OnTextInput(this, new TextInputEventArgs(c, key));
}
+ public void CallKeyDown(char c, Keys key = Keys.None)
+ {
+ OnKeyDown(this, new TextInputEventArgs(c, key));
+ }
+
public void CallTextEditing(string text, int start, int length)
{
OnTextEditing(this, new TextEditingEventArgs(text, start, length));
diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/TextInput.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/TextInput.cs
index df18c7bba..631097ac4 100644
--- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/TextInput.cs
+++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/TextInput.cs
@@ -4,6 +4,11 @@ namespace Microsoft.Xna.Framework
{
public static class TextInput
{
+ public static bool IsTextInputShown()
+ {
+ return Sdl.SDL_IsTextInputShown();
+ }
+
public static void StartTextInput()
{
Sdl.SDL_StartTextInput();
diff --git a/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Linux/x64/libSDL2-2.0.so.0 b/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Linux/x64/libSDL2-2.0.so.0
index 846fdc9a0..72cda07f7 100755
Binary files a/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Linux/x64/libSDL2-2.0.so.0 and b/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Linux/x64/libSDL2-2.0.so.0 differ
diff --git a/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Windows/x64/SDL2.dll b/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Windows/x64/SDL2.dll
index 6cf858caa..8c6230e6f 100644
Binary files a/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Windows/x64/SDL2.dll and b/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Windows/x64/SDL2.dll differ
diff --git a/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Windows/x86/SDL2.dll b/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Windows/x86/SDL2.dll
index a25b05a06..0de0af520 100644
Binary files a/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Windows/x86/SDL2.dll and b/Libraries/MonoGame.Framework/Src/ThirdParty/Dependencies/SDL/Windows/x86/SDL2.dll differ
diff --git a/WindowsSolution.sln b/WindowsSolution.sln
index d66f0eaf5..cff6db096 100644
--- a/WindowsSolution.sln
+++ b/WindowsSolution.sln
@@ -43,8 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsTest", "Barotrauma\B
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployAll", "Deploy\DeployAll\DeployAll.csproj", "{C98FE0D0-BC7D-4806-B592-734B53016FD8}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImeSharp", "Libraries\ImeSharp\ImeSharp.csproj", "{D42D8E48-A687-445D-AAF1-9E7E6EBF1DE6}"
-EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Libraries\GameAnalytics\GA-SDK-MONO-SHARED\GA-SDK-MONO-SHARED.projitems*{95c4d59d-9be4-4278-b4f8-46c0ba1a3916}*SharedItemsImports = 5