mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
InputText: Fixed multi-threading issues
This commit is contained in:
parent
76e504d1e5
commit
167123bcf7
@ -294,6 +294,9 @@ namespace InputText
|
||||
|
||||
public bool ShowInputBox()
|
||||
{
|
||||
if (this.drBackup != DialogResult.None)
|
||||
return false;
|
||||
|
||||
if (this._FocusDismiss)
|
||||
{
|
||||
this.Show(this.parent);
|
||||
@ -324,6 +327,12 @@ namespace InputText
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Abort()
|
||||
{
|
||||
this.drBackup = DialogResult.Cancel;
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void txtInput_Leave(object sender, EventArgs e)
|
||||
{
|
||||
if (this._FocusDismiss)
|
||||
|
@ -71,17 +71,51 @@ namespace InputText
|
||||
|
||||
if (go)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(ExecuteBangThread, args);
|
||||
ExecuteBangParam param = new ExecuteBangParam(args);
|
||||
if (ReadOptions(param)) // Read all options in advance for thread-safety
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(ExecuteBangThread, param);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No need to continue
|
||||
lock (this.locker)
|
||||
{
|
||||
this.IsExecuteBangRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExecuteBangParam
|
||||
{
|
||||
internal enum BangType
|
||||
{
|
||||
Unknown,
|
||||
SetVariable,
|
||||
ExecuteBatch
|
||||
};
|
||||
internal Dictionary<string, string> Options;
|
||||
internal List<string> Commands;
|
||||
internal string Command;
|
||||
internal BangType Type;
|
||||
|
||||
internal ExecuteBangParam(string args)
|
||||
{
|
||||
this.Options = new Dictionary<string, string>();
|
||||
this.Commands = new List<string>();
|
||||
this.Command = args.Trim();
|
||||
this.Type = BangType.Unknown;
|
||||
}
|
||||
};
|
||||
|
||||
private void ExecuteBangThread(object state)
|
||||
{
|
||||
string command = (string)state;
|
||||
ExecuteBangParam param = (ExecuteBangParam)state;
|
||||
|
||||
try
|
||||
{
|
||||
ExecuteCommands(command);
|
||||
ExecuteCommands(param);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -93,6 +127,11 @@ namespace InputText
|
||||
this.IsExecuteBangRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Dispose()
|
||||
{
|
||||
CloseInputBox();
|
||||
}
|
||||
}
|
||||
|
||||
#region Plugin
|
||||
@ -112,6 +151,7 @@ namespace InputText
|
||||
public unsafe static void Finalize(void* data)
|
||||
{
|
||||
uint id = (uint)data;
|
||||
Measures[id].Dispose();
|
||||
Measures.Remove(id);
|
||||
}
|
||||
|
||||
|
@ -25,59 +25,45 @@ namespace InputText
|
||||
{
|
||||
internal partial class Measure
|
||||
{
|
||||
private void ExecuteCommands(string Command)
|
||||
private bool ReadOptions(ExecuteBangParam param)
|
||||
{
|
||||
Command = Command.Trim();
|
||||
|
||||
// Get default options
|
||||
Dictionary<string, string> Options = new Dictionary<string, string>();
|
||||
ReadOption("DefaultValue", Options);
|
||||
ReadOption("X", Options);
|
||||
ReadOption("Y", Options);
|
||||
ReadOption("W", Options);
|
||||
ReadOption("H", Options);
|
||||
ReadOption("StringStyle", Options);
|
||||
ReadOption("StringAlign", Options);
|
||||
ReadOption("FocusDismiss", Options);
|
||||
ReadOption("FontColor", Options);
|
||||
ReadOption("FontFace", Options);
|
||||
ReadOption("FontSize", Options);
|
||||
ReadOption("SolidColor", Options);
|
||||
ReadOption("Password", Options);
|
||||
ReadOption("TopMost", Options);
|
||||
ReadOption("DefaultValue", param.Options);
|
||||
ReadOption("X", param.Options);
|
||||
ReadOption("Y", param.Options);
|
||||
ReadOption("W", param.Options);
|
||||
ReadOption("H", param.Options);
|
||||
ReadOption("StringStyle", param.Options);
|
||||
ReadOption("StringAlign", param.Options);
|
||||
ReadOption("FocusDismiss", param.Options);
|
||||
ReadOption("FontColor", param.Options);
|
||||
ReadOption("FontFace", param.Options);
|
||||
ReadOption("FontSize", param.Options);
|
||||
ReadOption("SolidColor", param.Options);
|
||||
ReadOption("Password", param.Options);
|
||||
ReadOption("TopMost", param.Options);
|
||||
|
||||
#region Handle a single parameter
|
||||
|
||||
// If our parameter list only contains a single word, then open a textbox immediately
|
||||
// and set a value. This mode does not do any batching.
|
||||
if (!Command.Contains(" "))
|
||||
if (!param.Command.Contains(" "))
|
||||
{
|
||||
// Assume that the parameter is the name of the variable
|
||||
|
||||
// Ask for input
|
||||
string sInput = GetUserInput(Options);
|
||||
|
||||
// If the user cancelled out of the inputbox (ESC key, etc.), then abort
|
||||
if (sInput == null)
|
||||
return;
|
||||
|
||||
// Ask Rainmeter to set the variable using a bang (http://rainmeter.net/RainCMS/?q=Bangs)
|
||||
API.Execute(rm.GetSkin(), "!SetVariable " + Command + " \"" + sInput + "\"");
|
||||
|
||||
// Note that the skin needs DynamicVariables=1 in the measure's settings or the above
|
||||
// code will have no effect.
|
||||
return;
|
||||
param.Type = ExecuteBangParam.BangType.SetVariable;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Handle multiple parameters
|
||||
|
||||
// Our parameter list contains at least two words, so split them up
|
||||
string[] sParts = Command.Split(new string[] { " " }, StringSplitOptions.None);
|
||||
string[] sParts = param.Command.Split(new string[] { " " }, StringSplitOptions.None);
|
||||
|
||||
// If the first parameter is 'ExecuteBatch' (not case sensitive)...
|
||||
if (sParts[0].Trim().ToUpper() == "EXECUTEBATCH")
|
||||
{
|
||||
param.Type = ExecuteBangParam.BangType.ExecuteBatch;
|
||||
|
||||
// ExecuteBatch tells this plugin to go through the measure's settings to look
|
||||
// for lines beginning with "Command" and executing Rainmeter bangs for each one.
|
||||
// If a line contains $UserInput$, then an input textbox is opened and command
|
||||
@ -131,8 +117,6 @@ namespace InputText
|
||||
#region Parse commands in range
|
||||
// Parse each command in the range, aborting if any line returns 'false' or
|
||||
// the requested command line does not exist in the config for that measure.
|
||||
List<string> commands = new List<string>();
|
||||
|
||||
for (int i = iMin; i <= iMax; i++)
|
||||
{
|
||||
// Read this command's line
|
||||
@ -142,28 +126,59 @@ namespace InputText
|
||||
if (string.IsNullOrEmpty(sCurrentLine))
|
||||
break;
|
||||
|
||||
commands.Add(sCurrentLine);
|
||||
}
|
||||
|
||||
foreach (string sCurrentLine in commands)
|
||||
{
|
||||
// Execute the line, but if there's a problem (error or they cancel the
|
||||
// input textbox), then abort
|
||||
if (!ExecuteLine(sCurrentLine, Options))
|
||||
break;
|
||||
|
||||
// Continue to the next line, if there is any
|
||||
param.Commands.Add(sCurrentLine);
|
||||
}
|
||||
#endregion
|
||||
return;
|
||||
}
|
||||
|
||||
// Unhandled command, log the message but otherwise do nothing
|
||||
API.Log(API.LogType.Debug, "InputText: Received command \"" + sParts[0].Trim() + "\", left unhandled");
|
||||
return param.Commands.Count > 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return;
|
||||
// Unhandled command, log the message but otherwise do nothing
|
||||
param.Type = ExecuteBangParam.BangType.Unknown;
|
||||
API.Log(API.LogType.Debug, "InputText: Received command \"" + sParts[0].Trim() + "\", left unhandled");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ExecuteCommands(ExecuteBangParam param)
|
||||
{
|
||||
switch (param.Type)
|
||||
{
|
||||
case ExecuteBangParam.BangType.SetVariable:
|
||||
{
|
||||
// Assume that the parameter is the name of the variable
|
||||
|
||||
// Ask for input
|
||||
string sInput = GetUserInput(param.Options);
|
||||
|
||||
// If the user cancelled out of the inputbox (ESC key, etc.), then abort
|
||||
if (sInput == null)
|
||||
break;
|
||||
|
||||
// Ask Rainmeter to set the variable using a bang (http://rainmeter.net/RainCMS/?q=Bangs)
|
||||
API.Execute(rm.GetSkin(), "!SetVariable " + param.Command + " \"" + sInput + "\"");
|
||||
|
||||
// Note that the skin needs DynamicVariables=1 in the measure's settings or the above
|
||||
// code will have no effect.
|
||||
}
|
||||
break;
|
||||
|
||||
case ExecuteBangParam.BangType.ExecuteBatch:
|
||||
{
|
||||
foreach (string sCurrentLine in param.Commands)
|
||||
{
|
||||
// Execute the line, but if there's a problem (error or they cancel the
|
||||
// input textbox), then abort
|
||||
if (!ExecuteLine(sCurrentLine, param.Options))
|
||||
break;
|
||||
|
||||
// Continue to the next line, if there is any
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region This is all code custom to this plugin
|
||||
@ -235,6 +250,9 @@ namespace InputText
|
||||
delegate void ChangeSettingFromString(string value);
|
||||
delegate void ChangeInputBoxSetting(string option, ChangeSettingFromString method);
|
||||
|
||||
private InputBox _InputBox = null;
|
||||
private object _InputBoxLocker = new object();
|
||||
|
||||
private string GetUserInput(Dictionary<string, string> Options)
|
||||
{
|
||||
// No INI overrides provided, so create an empty list
|
||||
@ -242,78 +260,108 @@ namespace InputText
|
||||
}
|
||||
private string GetUserInput(Dictionary<string, string> Options, Dictionary<string, string> Overrides)
|
||||
{
|
||||
SkinWindow skin = new SkinWindow(rm);
|
||||
InputBox input = null;
|
||||
|
||||
// Create the form. 'InputBox' is a .NET form with a textbox and two button controls on it.
|
||||
InputBox input = new InputBox(skin);
|
||||
input.ChangeX("0");
|
||||
input.ChangeY("0");
|
||||
|
||||
// Change the styles of the InputBox form based on overrides or INI values
|
||||
#region Style and preference tweaks (INI and override settings)
|
||||
|
||||
ChangeInputBoxSetting changeSetting = (opt, change) =>
|
||||
lock (this._InputBoxLocker)
|
||||
{
|
||||
if (Overrides.ContainsKey(opt))
|
||||
change(Overrides[opt]);
|
||||
else if (Options.ContainsKey(opt))
|
||||
change(Options[opt]);
|
||||
};
|
||||
SkinWindow skin = new SkinWindow(rm);
|
||||
|
||||
changeSetting("FontFace", input.ChangeFontFace);
|
||||
changeSetting("FontSize", input.ChangeFontSize);
|
||||
// Create the form. 'InputBox' is a .NET form with a textbox and two button controls on it.
|
||||
this._InputBox = new InputBox(skin);
|
||||
input = this._InputBox;
|
||||
|
||||
changeSetting("W", input.ChangeW);
|
||||
changeSetting("H", input.ChangeH);
|
||||
changeSetting("X", input.ChangeX);
|
||||
changeSetting("Y", input.ChangeY);
|
||||
input.ChangeX("0");
|
||||
input.ChangeY("0");
|
||||
|
||||
changeSetting("StringStyle", input.ChangeFontStringStyle);
|
||||
changeSetting("StringAlign", input.ChangeStringAlign);
|
||||
// Change the styles of the InputBox form based on overrides or INI values
|
||||
#region Style and preference tweaks (INI and override settings)
|
||||
|
||||
changeSetting("FontColor", input.ChangeFontColor);
|
||||
changeSetting("SolidColor", input.ChangeBackColor);
|
||||
ChangeInputBoxSetting changeSetting = (opt, change) =>
|
||||
{
|
||||
if (Overrides.ContainsKey(opt))
|
||||
change(Overrides[opt]);
|
||||
else if (Options.ContainsKey(opt))
|
||||
change(Options[opt]);
|
||||
};
|
||||
|
||||
if (Overrides.ContainsKey("FocusDismiss"))
|
||||
input.MakeFocusDismiss(Overrides["FocusDismiss"] == "1");
|
||||
else if (Options.ContainsKey("FocusDismiss"))
|
||||
input.MakeFocusDismiss(Options["FocusDismiss"].Trim() == "1");
|
||||
changeSetting("FontFace", input.ChangeFontFace);
|
||||
changeSetting("FontSize", input.ChangeFontSize);
|
||||
|
||||
if (Overrides.ContainsKey("Password"))
|
||||
input.MakePassword(Overrides["Password"] == "1");
|
||||
else if (Options.ContainsKey("Password"))
|
||||
input.MakePassword(Options["Password"].Trim() == "1");
|
||||
changeSetting("W", input.ChangeW);
|
||||
changeSetting("H", input.ChangeH);
|
||||
changeSetting("X", input.ChangeX);
|
||||
changeSetting("Y", input.ChangeY);
|
||||
|
||||
string topmost = null;
|
||||
if (Overrides.ContainsKey("TopMost"))
|
||||
topmost = Overrides["TopMost"];
|
||||
else if (Options.ContainsKey("TopMost"))
|
||||
topmost = Options["TopMost"].Trim();
|
||||
switch (topmost)
|
||||
{
|
||||
case "1":
|
||||
input.MakeTopmost();
|
||||
break;
|
||||
case "0":
|
||||
break;
|
||||
default: // AUTO
|
||||
if (skin.IsTopmost)
|
||||
changeSetting("StringStyle", input.ChangeFontStringStyle);
|
||||
changeSetting("StringAlign", input.ChangeStringAlign);
|
||||
|
||||
changeSetting("FontColor", input.ChangeFontColor);
|
||||
changeSetting("SolidColor", input.ChangeBackColor);
|
||||
|
||||
if (Overrides.ContainsKey("FocusDismiss"))
|
||||
input.MakeFocusDismiss(Overrides["FocusDismiss"] == "1");
|
||||
else if (Options.ContainsKey("FocusDismiss"))
|
||||
input.MakeFocusDismiss(Options["FocusDismiss"].Trim() == "1");
|
||||
|
||||
if (Overrides.ContainsKey("Password"))
|
||||
input.MakePassword(Overrides["Password"] == "1");
|
||||
else if (Options.ContainsKey("Password"))
|
||||
input.MakePassword(Options["Password"].Trim() == "1");
|
||||
|
||||
string topmost = null;
|
||||
if (Overrides.ContainsKey("TopMost"))
|
||||
topmost = Overrides["TopMost"];
|
||||
else if (Options.ContainsKey("TopMost"))
|
||||
topmost = Options["TopMost"].Trim();
|
||||
switch (topmost)
|
||||
{
|
||||
case "1":
|
||||
input.MakeTopmost();
|
||||
break;
|
||||
break;
|
||||
case "0":
|
||||
break;
|
||||
default: // AUTO
|
||||
if (skin.IsTopmost)
|
||||
input.MakeTopmost();
|
||||
break;
|
||||
}
|
||||
|
||||
changeSetting("DefaultValue", input.DefaultValue);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
changeSetting("DefaultValue", input.DefaultValue);
|
||||
string result = null;
|
||||
|
||||
#endregion
|
||||
|
||||
if (!input.ShowInputBox())
|
||||
return null;
|
||||
|
||||
lock (this.locker)
|
||||
if (input.ShowInputBox())
|
||||
{
|
||||
this.LastInput = input.TextValue;
|
||||
lock (this.locker)
|
||||
{
|
||||
this.LastInput = input.TextValue;
|
||||
result = this.LastInput;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispose
|
||||
input = null;
|
||||
lock (this._InputBoxLocker)
|
||||
{
|
||||
this._InputBox.Dispose();
|
||||
this._InputBox = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CloseInputBox()
|
||||
{
|
||||
lock (this._InputBoxLocker)
|
||||
{
|
||||
if (this._InputBox != null)
|
||||
{
|
||||
this._InputBox.Abort();
|
||||
System.Threading.Thread.Sleep(50); // Wait for closing input box
|
||||
}
|
||||
}
|
||||
return input.TextValue;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user