Project updates for OLED
This commit is contained in:
21
PCPalConfigurator/App.config
Normal file
21
PCPalConfigurator/App.config
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
|
||||||
|
<section name="PCPalConfigurator.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||||
|
<section name="PCPalConfigurator.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||||
|
</sectionGroup>
|
||||||
|
</configSections>
|
||||||
|
<userSettings>
|
||||||
|
<PCPalConfigurator.Properties.Settings>
|
||||||
|
<setting name="LastIconDirectory" serializeAs="String">
|
||||||
|
<value></value>
|
||||||
|
</setting>
|
||||||
|
</PCPalConfigurator.Properties.Settings>
|
||||||
|
<PCPalConfigurator.Settings>
|
||||||
|
<setting name="LastIconDirectory" serializeAs="String">
|
||||||
|
<value />
|
||||||
|
</setting>
|
||||||
|
</PCPalConfigurator.Settings>
|
||||||
|
</userSettings>
|
||||||
|
</configuration>
|
||||||
@@ -2,8 +2,11 @@
|
|||||||
{
|
{
|
||||||
public class ConfigData
|
public class ConfigData
|
||||||
{
|
{
|
||||||
|
// Common settings
|
||||||
public string LastUsedPort { get; set; }
|
public string LastUsedPort { get; set; }
|
||||||
public string ScreenType { get; set; }
|
public string ScreenType { get; set; } // "1602", "TFT4_6", or "OLED"
|
||||||
|
|
||||||
|
// LCD-specific settings
|
||||||
public string Line1Selection { get; set; }
|
public string Line1Selection { get; set; }
|
||||||
public string Line1CustomText { get; set; }
|
public string Line1CustomText { get; set; }
|
||||||
public string Line2Selection { get; set; }
|
public string Line2Selection { get; set; }
|
||||||
@@ -11,5 +14,8 @@
|
|||||||
public string Line1PostText { get; set; }
|
public string Line1PostText { get; set; }
|
||||||
public string Line2PostText { get; set; }
|
public string Line2PostText { get; set; }
|
||||||
|
|
||||||
|
// OLED-specific settings
|
||||||
|
public string OledMarkup { get; set; }
|
||||||
|
public string LastIconDirectory { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
133
PCPalConfigurator/ConfiguratorForm.Designer.cs
generated
133
PCPalConfigurator/ConfiguratorForm.Designer.cs
generated
@@ -7,6 +7,8 @@
|
|||||||
private TabControl tabControl;
|
private TabControl tabControl;
|
||||||
private TabPage tab1602;
|
private TabPage tab1602;
|
||||||
private TabPage tabTFT;
|
private TabPage tabTFT;
|
||||||
|
private TabPage tabOLED;
|
||||||
|
private TabPage tabHelp;
|
||||||
private TabPage tabAbout;
|
private TabPage tabAbout;
|
||||||
|
|
||||||
private ComboBox cmbLine1;
|
private ComboBox cmbLine1;
|
||||||
@@ -15,7 +17,7 @@
|
|||||||
private ComboBox cmbLine2;
|
private ComboBox cmbLine2;
|
||||||
private TextBox txtLine2;
|
private TextBox txtLine2;
|
||||||
private TextBox txtLine2Post;
|
private TextBox txtLine2Post;
|
||||||
private Button btnApply;
|
private Button btnSave;
|
||||||
|
|
||||||
private Label lblLine1;
|
private Label lblLine1;
|
||||||
private Label lblLine1Prefix;
|
private Label lblLine1Prefix;
|
||||||
@@ -24,6 +26,19 @@
|
|||||||
private Label lblLine2Prefix;
|
private Label lblLine2Prefix;
|
||||||
private Label lblLine2Suffix;
|
private Label lblLine2Suffix;
|
||||||
|
|
||||||
|
// OLED tab controls
|
||||||
|
private TextBox txtOledMarkup;
|
||||||
|
private Panel panelOledButtons;
|
||||||
|
private Button btnInsertIcon;
|
||||||
|
private Button btnLoadOledExample;
|
||||||
|
private Button btnSaveOled;
|
||||||
|
private Panel panelOledPreview;
|
||||||
|
private Button btnPreview;
|
||||||
|
private Label lblPreviewHeader;
|
||||||
|
|
||||||
|
// Help tab controls
|
||||||
|
private TextBox txtHelpContent;
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing && (components != null))
|
if (disposing && (components != null))
|
||||||
@@ -37,6 +52,8 @@
|
|||||||
this.tabControl = new TabControl();
|
this.tabControl = new TabControl();
|
||||||
this.tab1602 = new TabPage("1602 LCD");
|
this.tab1602 = new TabPage("1602 LCD");
|
||||||
this.tabTFT = new TabPage("4.6\" TFT LCD");
|
this.tabTFT = new TabPage("4.6\" TFT LCD");
|
||||||
|
this.tabOLED = new TabPage("OLED Display");
|
||||||
|
this.tabHelp = new TabPage("Help");
|
||||||
this.tabAbout = new TabPage("About");
|
this.tabAbout = new TabPage("About");
|
||||||
|
|
||||||
this.cmbLine1 = new ComboBox();
|
this.cmbLine1 = new ComboBox();
|
||||||
@@ -45,7 +62,7 @@
|
|||||||
this.cmbLine2 = new ComboBox();
|
this.cmbLine2 = new ComboBox();
|
||||||
this.txtLine2 = new TextBox();
|
this.txtLine2 = new TextBox();
|
||||||
this.txtLine2Post = new TextBox();
|
this.txtLine2Post = new TextBox();
|
||||||
this.btnApply = new Button();
|
this.btnSave = new Button();
|
||||||
|
|
||||||
this.lblLine1 = new Label();
|
this.lblLine1 = new Label();
|
||||||
this.lblLine1Prefix = new Label();
|
this.lblLine1Prefix = new Label();
|
||||||
@@ -54,11 +71,25 @@
|
|||||||
this.lblLine2Prefix = new Label();
|
this.lblLine2Prefix = new Label();
|
||||||
this.lblLine2Suffix = new Label();
|
this.lblLine2Suffix = new Label();
|
||||||
|
|
||||||
|
// OLED tab controls
|
||||||
|
this.txtOledMarkup = new TextBox();
|
||||||
|
this.panelOledButtons = new Panel();
|
||||||
|
this.btnInsertIcon = new Button();
|
||||||
|
this.btnLoadOledExample = new Button();
|
||||||
|
this.btnSaveOled = new Button();
|
||||||
|
this.panelOledPreview = new Panel();
|
||||||
|
this.btnPreview = new Button();
|
||||||
|
this.lblPreviewHeader = new Label();
|
||||||
|
|
||||||
|
// Help tab controls
|
||||||
|
this.txtHelpContent = new TextBox();
|
||||||
|
|
||||||
// === TabControl ===
|
// === TabControl ===
|
||||||
this.tabControl.Location = new System.Drawing.Point(10, 10);
|
this.tabControl.Dock = DockStyle.Fill;
|
||||||
this.tabControl.Size = new System.Drawing.Size(620, 280);
|
|
||||||
this.tabControl.TabPages.Add(this.tab1602);
|
this.tabControl.TabPages.Add(this.tab1602);
|
||||||
this.tabControl.TabPages.Add(this.tabTFT);
|
this.tabControl.TabPages.Add(this.tabTFT);
|
||||||
|
this.tabControl.TabPages.Add(this.tabOLED);
|
||||||
|
this.tabControl.TabPages.Add(this.tabHelp);
|
||||||
this.tabControl.TabPages.Add(this.tabAbout);
|
this.tabControl.TabPages.Add(this.tabAbout);
|
||||||
this.Controls.Add(this.tabControl);
|
this.Controls.Add(this.tabControl);
|
||||||
|
|
||||||
@@ -106,20 +137,98 @@
|
|||||||
this.txtLine2Post.Location = new System.Drawing.Point(420, 118);
|
this.txtLine2Post.Location = new System.Drawing.Point(420, 118);
|
||||||
this.txtLine2Post.Size = new System.Drawing.Size(120, 21);
|
this.txtLine2Post.Size = new System.Drawing.Size(120, 21);
|
||||||
|
|
||||||
// === Apply Button ===
|
// === Save Button (renamed from Apply) ===
|
||||||
this.btnApply.Text = "Apply";
|
this.btnSave.Text = "Save";
|
||||||
this.btnApply.Location = new System.Drawing.Point(20, 170);
|
this.btnSave.Location = new System.Drawing.Point(20, 170);
|
||||||
this.btnApply.Size = new System.Drawing.Size(100, 30);
|
this.btnSave.Size = new System.Drawing.Size(100, 30);
|
||||||
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
|
this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
|
||||||
|
|
||||||
// === Add to 1602 Tab ===
|
// === Add to 1602 Tab ===
|
||||||
this.tab1602.Controls.AddRange(new Control[]
|
this.tab1602.Controls.AddRange(new Control[]
|
||||||
{
|
{
|
||||||
lblLine1, cmbLine1, lblLine1Prefix, txtLine1, lblLine1Suffix, txtLine1Post,
|
lblLine1, cmbLine1, lblLine1Prefix, txtLine1, lblLine1Suffix, txtLine1Post,
|
||||||
lblLine2, cmbLine2, lblLine2Prefix, txtLine2, lblLine2Suffix, txtLine2Post,
|
lblLine2, cmbLine2, lblLine2Prefix, txtLine2, lblLine2Suffix, txtLine2Post,
|
||||||
btnApply
|
btnSave
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// === OLED Tab Setup ===
|
||||||
|
// Main panel for layout
|
||||||
|
Panel oledMainPanel = new Panel();
|
||||||
|
oledMainPanel.Dock = DockStyle.Fill;
|
||||||
|
this.tabOLED.Controls.Add(oledMainPanel);
|
||||||
|
|
||||||
|
// Markup editor
|
||||||
|
this.txtOledMarkup = new TextBox();
|
||||||
|
this.txtOledMarkup.Multiline = true;
|
||||||
|
this.txtOledMarkup.ScrollBars = ScrollBars.Vertical;
|
||||||
|
this.txtOledMarkup.Font = new Font("Consolas", 9.75F);
|
||||||
|
this.txtOledMarkup.TextChanged += new EventHandler(this.txtOledMarkup_TextChanged);
|
||||||
|
this.txtOledMarkup.Size = new Size(580, 130);
|
||||||
|
this.txtOledMarkup.Location = new Point(10, 10);
|
||||||
|
oledMainPanel.Controls.Add(this.txtOledMarkup);
|
||||||
|
|
||||||
|
// Button panel
|
||||||
|
this.panelOledButtons = new Panel();
|
||||||
|
this.panelOledButtons.Size = new Size(580, 40);
|
||||||
|
this.panelOledButtons.Location = new Point(10, 150);
|
||||||
|
oledMainPanel.Controls.Add(this.panelOledButtons);
|
||||||
|
|
||||||
|
this.btnInsertIcon = new Button();
|
||||||
|
this.btnInsertIcon.Text = "Insert Icon";
|
||||||
|
this.btnInsertIcon.Location = new Point(0, 7);
|
||||||
|
this.btnInsertIcon.Size = new Size(120, 26);
|
||||||
|
this.btnInsertIcon.Click += new EventHandler(this.btnInsertIcon_Click);
|
||||||
|
this.panelOledButtons.Controls.Add(this.btnInsertIcon);
|
||||||
|
|
||||||
|
this.btnLoadOledExample = new Button();
|
||||||
|
this.btnLoadOledExample.Text = "Load Example";
|
||||||
|
this.btnLoadOledExample.Location = new Point(130, 7);
|
||||||
|
this.btnLoadOledExample.Size = new Size(120, 26);
|
||||||
|
this.btnLoadOledExample.Click += new EventHandler(this.btnLoadOledExample_Click);
|
||||||
|
this.panelOledButtons.Controls.Add(this.btnLoadOledExample);
|
||||||
|
|
||||||
|
this.btnPreview = new Button();
|
||||||
|
this.btnPreview.Text = "Update Preview";
|
||||||
|
this.btnPreview.Location = new Point(260, 7);
|
||||||
|
this.btnPreview.Size = new Size(120, 26);
|
||||||
|
this.btnPreview.Click += new EventHandler(this.btnPreview_Click);
|
||||||
|
this.panelOledButtons.Controls.Add(this.btnPreview);
|
||||||
|
|
||||||
|
this.btnSaveOled = new Button();
|
||||||
|
this.btnSaveOled.Text = "Save";
|
||||||
|
this.btnSaveOled.Location = new Point(480, 7);
|
||||||
|
this.btnSaveOled.Size = new Size(100, 26);
|
||||||
|
this.btnSaveOled.Click += new EventHandler(this.btnSave_Click); // Uses same event handler
|
||||||
|
this.panelOledButtons.Controls.Add(this.btnSaveOled);
|
||||||
|
|
||||||
|
// Preview header
|
||||||
|
this.lblPreviewHeader = new Label();
|
||||||
|
this.lblPreviewHeader.Text = "OLED Preview (256x64)";
|
||||||
|
this.lblPreviewHeader.Location = new Point(10, 195);
|
||||||
|
this.lblPreviewHeader.Size = new Size(580, 20);
|
||||||
|
this.lblPreviewHeader.TextAlign = ContentAlignment.MiddleCenter;
|
||||||
|
this.lblPreviewHeader.BackColor = System.Drawing.SystemColors.ControlLight;
|
||||||
|
this.lblPreviewHeader.BorderStyle = BorderStyle.FixedSingle;
|
||||||
|
oledMainPanel.Controls.Add(this.lblPreviewHeader);
|
||||||
|
|
||||||
|
// Preview panel
|
||||||
|
this.panelOledPreview = new Panel();
|
||||||
|
this.panelOledPreview.Location = new Point(10, 215);
|
||||||
|
this.panelOledPreview.Size = new Size(580, 110);
|
||||||
|
this.panelOledPreview.BackColor = System.Drawing.Color.Black;
|
||||||
|
this.panelOledPreview.BorderStyle = BorderStyle.FixedSingle;
|
||||||
|
this.panelOledPreview.Paint += new PaintEventHandler(this.panelOledPreview_Paint);
|
||||||
|
oledMainPanel.Controls.Add(this.panelOledPreview);
|
||||||
|
|
||||||
|
// === Help Tab Setup ===
|
||||||
|
this.txtHelpContent = new TextBox();
|
||||||
|
this.txtHelpContent.Dock = DockStyle.Fill;
|
||||||
|
this.txtHelpContent.Multiline = true;
|
||||||
|
this.txtHelpContent.ReadOnly = true;
|
||||||
|
this.txtHelpContent.ScrollBars = ScrollBars.Vertical;
|
||||||
|
this.txtHelpContent.Font = new Font("Segoe UI", 9F);
|
||||||
|
this.tabHelp.Controls.Add(this.txtHelpContent);
|
||||||
|
|
||||||
// === TFT Tab Placeholder ===
|
// === TFT Tab Placeholder ===
|
||||||
Label lblTFT = new Label()
|
Label lblTFT = new Label()
|
||||||
{
|
{
|
||||||
@@ -140,9 +249,9 @@
|
|||||||
|
|
||||||
// === Form ===
|
// === Form ===
|
||||||
this.Text = "Display Configurator";
|
this.Text = "Display Configurator";
|
||||||
this.ClientSize = new System.Drawing.Size(640, 300);
|
this.ClientSize = new System.Drawing.Size(600, 380); // Adjusted size for compact layout
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using LibreHardwareMonitor.Hardware;
|
using LibreHardwareMonitor.Hardware;
|
||||||
using PCPalConfigurator;
|
|
||||||
|
|
||||||
namespace PCPalConfigurator
|
namespace PCPalConfigurator
|
||||||
{
|
{
|
||||||
@@ -12,12 +15,129 @@ namespace PCPalConfigurator
|
|||||||
private ConfigData config;
|
private ConfigData config;
|
||||||
private readonly string ConfigFile = GetConfigPath();
|
private readonly string ConfigFile = GetConfigPath();
|
||||||
private Computer computer;
|
private Computer computer;
|
||||||
|
private System.Windows.Forms.Timer sensorUpdateTimer;
|
||||||
|
|
||||||
|
// Preview rendering data
|
||||||
|
private List<PreviewElement> previewElements = new List<PreviewElement>();
|
||||||
|
private Dictionary<string, float> sensorValues = new Dictionary<string, float>();
|
||||||
|
private bool autoUpdatePreview = true;
|
||||||
|
private const int OledWidth = 256;
|
||||||
|
private const int OledHeight = 64;
|
||||||
|
|
||||||
|
// Classes for preview rendering
|
||||||
|
private abstract class PreviewElement
|
||||||
|
{
|
||||||
|
public abstract void Draw(Graphics g);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TextElement : PreviewElement
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
public int Y { get; set; }
|
||||||
|
public int Size { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
public override void Draw(Graphics g)
|
||||||
|
{
|
||||||
|
// Choose font size based on the size parameter
|
||||||
|
Font font;
|
||||||
|
switch (Size)
|
||||||
|
{
|
||||||
|
case 1: font = new Font("Consolas", 8); break;
|
||||||
|
case 2: font = new Font("Consolas", 10); break;
|
||||||
|
case 3: font = new Font("Consolas", 12); break;
|
||||||
|
default: font = new Font("Consolas", 8); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw text with white color
|
||||||
|
g.DrawString(Text, font, Brushes.White, X, Y - font.Height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BarElement : PreviewElement
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
public int Y { get; set; }
|
||||||
|
public int Width { get; set; }
|
||||||
|
public int Height { get; set; }
|
||||||
|
public int Value { get; set; }
|
||||||
|
|
||||||
|
public override void Draw(Graphics g)
|
||||||
|
{
|
||||||
|
// Draw outline rectangle
|
||||||
|
g.DrawRectangle(Pens.White, X, Y, Width, Height);
|
||||||
|
|
||||||
|
// Calculate fill width based on value (0-100)
|
||||||
|
int fillWidth = (int)(Width * (Value / 100.0));
|
||||||
|
if (fillWidth > 0)
|
||||||
|
{
|
||||||
|
// Draw filled portion
|
||||||
|
g.FillRectangle(Brushes.White, X + 1, Y + 1, fillWidth - 1, Height - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RectElement : PreviewElement
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
public int Y { get; set; }
|
||||||
|
public int Width { get; set; }
|
||||||
|
public int Height { get; set; }
|
||||||
|
public bool Filled { get; set; }
|
||||||
|
|
||||||
|
public override void Draw(Graphics g)
|
||||||
|
{
|
||||||
|
if (Filled)
|
||||||
|
{
|
||||||
|
// Draw filled box
|
||||||
|
g.FillRectangle(Brushes.White, X, Y, Width, Height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Draw outline rectangle
|
||||||
|
g.DrawRectangle(Pens.White, X, Y, Width, Height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LineElement : PreviewElement
|
||||||
|
{
|
||||||
|
public int X1 { get; set; }
|
||||||
|
public int Y1 { get; set; }
|
||||||
|
public int X2 { get; set; }
|
||||||
|
public int Y2 { get; set; }
|
||||||
|
|
||||||
|
public override void Draw(Graphics g)
|
||||||
|
{
|
||||||
|
g.DrawLine(Pens.White, X1, Y1, X2, Y2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class IconElement : PreviewElement
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
public int Y { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public override void Draw(Graphics g)
|
||||||
|
{
|
||||||
|
// For preview, just draw a placeholder rectangle with icon name
|
||||||
|
g.DrawRectangle(Pens.Gray, X, Y, 24, 24);
|
||||||
|
using (Font font = new Font("Arial", 6))
|
||||||
|
{
|
||||||
|
g.DrawString(Name, font, Brushes.White, X + 2, Y + 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ConfiguratorForm()
|
public ConfiguratorForm()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
InitHardware();
|
InitHardware();
|
||||||
|
InitSensorTimer();
|
||||||
LoadConfig();
|
LoadConfig();
|
||||||
|
PopulateHelpContent();
|
||||||
|
UpdatePreview(); // Initial preview rendering
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitHardware()
|
private void InitHardware()
|
||||||
@@ -33,6 +153,44 @@ namespace PCPalConfigurator
|
|||||||
};
|
};
|
||||||
computer.Open();
|
computer.Open();
|
||||||
PopulateSensorOptions();
|
PopulateSensorOptions();
|
||||||
|
UpdateSensorValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitSensorTimer()
|
||||||
|
{
|
||||||
|
// Create a timer to regularly update sensor values for the preview
|
||||||
|
sensorUpdateTimer = new System.Windows.Forms.Timer();
|
||||||
|
sensorUpdateTimer.Interval = 1000; // Update every second
|
||||||
|
sensorUpdateTimer.Tick += SensorUpdateTimer_Tick;
|
||||||
|
sensorUpdateTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SensorUpdateTimer_Tick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Only update when the OLED tab is selected to save resources
|
||||||
|
if (tabControl.SelectedTab == tabOLED)
|
||||||
|
{
|
||||||
|
UpdateSensorValues();
|
||||||
|
UpdatePreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSensorValues()
|
||||||
|
{
|
||||||
|
// Update all hardware readings
|
||||||
|
foreach (var hardware in computer.Hardware)
|
||||||
|
{
|
||||||
|
hardware.Update();
|
||||||
|
|
||||||
|
foreach (var sensor in hardware.Sensors)
|
||||||
|
{
|
||||||
|
if (sensor.Value.HasValue)
|
||||||
|
{
|
||||||
|
string sensorId = GetSensorVariableName(hardware.Name, sensor.Name);
|
||||||
|
sensorValues[sensorId] = sensor.Value.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetConfigPath()
|
private static string GetConfigPath()
|
||||||
@@ -58,22 +216,37 @@ namespace PCPalConfigurator
|
|||||||
|
|
||||||
private void ApplyConfig()
|
private void ApplyConfig()
|
||||||
{
|
{
|
||||||
|
// LCD settings
|
||||||
cmbLine1.SelectedItem = config.Line1Selection;
|
cmbLine1.SelectedItem = config.Line1Selection;
|
||||||
txtLine1.Text = config.Line1CustomText;
|
txtLine1.Text = config.Line1CustomText;
|
||||||
txtLine1Post.Text = config.Line1PostText;
|
txtLine1Post.Text = config.Line1PostText;
|
||||||
cmbLine2.SelectedItem = config.Line2Selection;
|
cmbLine2.SelectedItem = config.Line2Selection;
|
||||||
txtLine2.Text = config.Line2CustomText;
|
txtLine2.Text = config.Line2CustomText;
|
||||||
txtLine2Post.Text = config.Line2PostText;
|
txtLine2Post.Text = config.Line2PostText;
|
||||||
|
|
||||||
|
// OLED settings
|
||||||
|
txtOledMarkup.Text = config.OledMarkup;
|
||||||
|
|
||||||
|
// Select the appropriate tab based on screen type
|
||||||
|
if (config.ScreenType == "1602")
|
||||||
|
tabControl.SelectedTab = tab1602;
|
||||||
|
else if (config.ScreenType == "TFT4_6")
|
||||||
|
tabControl.SelectedTab = tabTFT;
|
||||||
|
else if (config.ScreenType == "OLED")
|
||||||
|
tabControl.SelectedTab = tabOLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveConfig()
|
private void SaveConfig()
|
||||||
{
|
{
|
||||||
// ScreenType based on selected tab
|
// Determine screen type based on selected tab
|
||||||
if (tabControl.SelectedTab == tab1602)
|
if (tabControl.SelectedTab == tab1602)
|
||||||
config.ScreenType = "1602";
|
config.ScreenType = "1602";
|
||||||
else if (tabControl.SelectedTab == tabTFT)
|
else if (tabControl.SelectedTab == tabTFT)
|
||||||
config.ScreenType = "TFT4_6";
|
config.ScreenType = "TFT4_6";
|
||||||
|
else if (tabControl.SelectedTab == tabOLED)
|
||||||
|
config.ScreenType = "OLED";
|
||||||
|
|
||||||
|
// Save LCD settings
|
||||||
config.Line1Selection = cmbLine1.SelectedItem?.ToString();
|
config.Line1Selection = cmbLine1.SelectedItem?.ToString();
|
||||||
config.Line1CustomText = txtLine1.Text;
|
config.Line1CustomText = txtLine1.Text;
|
||||||
config.Line1PostText = txtLine1Post.Text;
|
config.Line1PostText = txtLine1Post.Text;
|
||||||
@@ -81,12 +254,16 @@ namespace PCPalConfigurator
|
|||||||
config.Line2CustomText = txtLine2.Text;
|
config.Line2CustomText = txtLine2.Text;
|
||||||
config.Line2PostText = txtLine2Post.Text;
|
config.Line2PostText = txtLine2Post.Text;
|
||||||
|
|
||||||
|
// Save OLED settings
|
||||||
|
config.OledMarkup = txtOledMarkup.Text;
|
||||||
|
|
||||||
string json = JsonConvert.SerializeObject(config, Formatting.Indented);
|
string json = JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||||
File.WriteAllText(ConfigFile, json);
|
File.WriteAllText(ConfigFile, json);
|
||||||
MessageBox.Show("Settings saved!");
|
MessageBox.Show("Settings saved!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnApply_Click(object sender, EventArgs e)
|
// Renamed from btnApply_Click to btnSave_Click
|
||||||
|
private void btnSave_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
SaveConfig();
|
SaveConfig();
|
||||||
}
|
}
|
||||||
@@ -116,5 +293,429 @@ namespace PCPalConfigurator
|
|||||||
cmbLine1.Items.Add("Custom Text");
|
cmbLine1.Items.Add("Custom Text");
|
||||||
cmbLine2.Items.Add("Custom Text");
|
cmbLine2.Items.Add("Custom Text");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PopulateHelpContent()
|
||||||
|
{
|
||||||
|
// Create help content for both LCD and OLED displays
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
// General Information
|
||||||
|
sb.AppendLine("PCPal Display Configurator Help");
|
||||||
|
sb.AppendLine("==============================");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// LCD Display Help
|
||||||
|
sb.AppendLine("LCD DISPLAY CONFIGURATION");
|
||||||
|
sb.AppendLine("------------------------");
|
||||||
|
sb.AppendLine("1. Select a sensor for each line or choose 'Custom Text'");
|
||||||
|
sb.AppendLine("2. Set prefix text to appear before the sensor value");
|
||||||
|
sb.AppendLine("3. Set suffix/units to appear after the sensor value");
|
||||||
|
sb.AppendLine("4. Click 'Save' to apply your settings");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// OLED Display Help
|
||||||
|
sb.AppendLine("OLED MARKUP REFERENCE");
|
||||||
|
sb.AppendLine("---------------------");
|
||||||
|
sb.AppendLine("The OLED display accepts markup tags to create your layout.");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Text: <text x=0 y=10 size=1>Hello World</text>");
|
||||||
|
sb.AppendLine(" - x, y: position coordinates (0,0 is top-left)");
|
||||||
|
sb.AppendLine(" - size: 1-3 (small to large)");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Progress Bar: <bar x=0 y=20 w=100 h=8 val=75 />");
|
||||||
|
sb.AppendLine(" - x, y: position");
|
||||||
|
sb.AppendLine(" - w, h: width and height");
|
||||||
|
sb.AppendLine(" - val: value 0-100");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Icon: <icon x=0 y=0 name=cpu />");
|
||||||
|
sb.AppendLine(" - Inserts a bitmap icon from the SD card");
|
||||||
|
sb.AppendLine(" - Use the 'Insert Icon' button to browse available icons");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Rectangle (outline): <rect x=0 y=0 w=20 h=10 />");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Filled Box: <box x=0 y=0 w=20 h=10 />");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Line: <line x1=0 y1=0 x2=20 y2=20 />");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Sensor variables
|
||||||
|
sb.AppendLine("SENSOR VARIABLES");
|
||||||
|
sb.AppendLine("----------------");
|
||||||
|
sb.AppendLine("Use {SensorName} syntax to include sensor values.");
|
||||||
|
sb.AppendLine("Example: <text x=0 y=10>CPU: {CPU_Core_i7_Total_Load}%</text>");
|
||||||
|
|
||||||
|
// Add available sensor variables with their current values
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Available sensor variables:");
|
||||||
|
|
||||||
|
var sensors = new Dictionary<string, Tuple<float, string, SensorType>>();
|
||||||
|
|
||||||
|
foreach (var hardware in computer.Hardware)
|
||||||
|
{
|
||||||
|
hardware.Update();
|
||||||
|
foreach (var sensor in hardware.Sensors)
|
||||||
|
{
|
||||||
|
if (sensor.Value.HasValue &&
|
||||||
|
(sensor.SensorType == SensorType.Load ||
|
||||||
|
sensor.SensorType == SensorType.Temperature ||
|
||||||
|
sensor.SensorType == SensorType.Clock ||
|
||||||
|
sensor.SensorType == SensorType.Fan ||
|
||||||
|
sensor.SensorType == SensorType.Power ||
|
||||||
|
sensor.SensorType == SensorType.Data))
|
||||||
|
{
|
||||||
|
string sensorId = GetSensorVariableName(hardware.Name, sensor.Name);
|
||||||
|
sensors[sensorId] = new Tuple<float, string, SensorType>(
|
||||||
|
sensor.Value.Value,
|
||||||
|
$"{hardware.Name} {sensor.Name}",
|
||||||
|
sensor.SensorType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group sensors by type for easier reading
|
||||||
|
var sensorGroups = sensors.GroupBy(s => s.Value.Item3);
|
||||||
|
foreach (var group in sensorGroups)
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine($"-- {group.Key} Sensors --");
|
||||||
|
|
||||||
|
foreach (var sensor in group)
|
||||||
|
{
|
||||||
|
string unit = GetSensorUnit(group.Key);
|
||||||
|
string value = FormatSensorValue(sensor.Value.Item1, group.Key);
|
||||||
|
sb.AppendLine($"{{{sensor.Key}}} = {value}{unit} ({sensor.Value.Item2})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txtHelpContent.Text = sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSensorUnit(SensorType sensorType)
|
||||||
|
{
|
||||||
|
return sensorType switch
|
||||||
|
{
|
||||||
|
SensorType.Temperature => "°C",
|
||||||
|
SensorType.Load => "%",
|
||||||
|
SensorType.Clock => "MHz",
|
||||||
|
SensorType.Power => "W",
|
||||||
|
SensorType.Fan => "RPM",
|
||||||
|
SensorType.Flow => "L/h",
|
||||||
|
SensorType.Control => "%",
|
||||||
|
SensorType.Level => "%",
|
||||||
|
_ => ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatSensorValue(float value, SensorType sensorType)
|
||||||
|
{
|
||||||
|
// Format different sensor types appropriately
|
||||||
|
return sensorType switch
|
||||||
|
{
|
||||||
|
SensorType.Temperature => value.ToString("F1"),
|
||||||
|
SensorType.Clock => value.ToString("F0"),
|
||||||
|
SensorType.Load => value.ToString("F1"),
|
||||||
|
SensorType.Fan => value.ToString("F0"),
|
||||||
|
SensorType.Power => value.ToString("F1"),
|
||||||
|
SensorType.Data => (value > 1024) ? (value / 1024).ToString("F1") : value.ToString("F1"),
|
||||||
|
_ => value.ToString("F1")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSensorVariableName(string hardwareName, string sensorName)
|
||||||
|
{
|
||||||
|
// Create a simplified and safe variable name
|
||||||
|
string name = $"{hardwareName}_{sensorName}"
|
||||||
|
.Replace(" ", "_")
|
||||||
|
.Replace("%", "Percent")
|
||||||
|
.Replace("#", "Num")
|
||||||
|
.Replace("/", "_")
|
||||||
|
.Replace("\\", "_")
|
||||||
|
.Replace("(", "")
|
||||||
|
.Replace(")", "")
|
||||||
|
.Replace(",", "");
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnInsertIcon_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using IconBrowser browser = new();
|
||||||
|
if (browser.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
// Get the icon markup and insert it at the cursor position
|
||||||
|
string iconMarkup = browser.GetIconMarkup();
|
||||||
|
if (!string.IsNullOrEmpty(iconMarkup))
|
||||||
|
{
|
||||||
|
int selectionStart = txtOledMarkup.SelectionStart;
|
||||||
|
txtOledMarkup.Text = txtOledMarkup.Text.Insert(selectionStart, iconMarkup);
|
||||||
|
txtOledMarkup.SelectionStart = selectionStart + iconMarkup.Length;
|
||||||
|
txtOledMarkup.Focus();
|
||||||
|
|
||||||
|
// Update preview
|
||||||
|
if (autoUpdatePreview)
|
||||||
|
UpdatePreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnLoadOledExample_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Create an appropriate example based on the user's hardware
|
||||||
|
string exampleMarkup = "";
|
||||||
|
string cpuLoad = FindFirstSensorOfType(HardwareType.Cpu, SensorType.Load);
|
||||||
|
string cpuTemp = FindFirstSensorOfType(HardwareType.Cpu, SensorType.Temperature);
|
||||||
|
string gpuLoad = FindFirstSensorOfType(HardwareType.GpuNvidia, SensorType.Load);
|
||||||
|
string gpuTemp = FindFirstSensorOfType(HardwareType.GpuNvidia, SensorType.Temperature);
|
||||||
|
string ramUsed = FindFirstSensorOfType(HardwareType.Memory, SensorType.Data);
|
||||||
|
|
||||||
|
exampleMarkup = "<text x=0 y=12 size=2>System Monitor</text>\n";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(cpuLoad) && !string.IsNullOrEmpty(cpuTemp))
|
||||||
|
{
|
||||||
|
exampleMarkup += $"<text x=0 y=30 size=1>CPU: {{{cpuLoad}}}% ({{{cpuTemp}}}°C)</text>\n";
|
||||||
|
exampleMarkup += $"<bar x=0 y=35 w=128 h=6 val={{{cpuLoad}}} />\n";
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(cpuLoad))
|
||||||
|
{
|
||||||
|
exampleMarkup += $"<text x=0 y=30 size=1>CPU: {{{cpuLoad}}}%</text>\n";
|
||||||
|
exampleMarkup += $"<bar x=0 y=35 w=128 h=6 val={{{cpuLoad}}} />\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(gpuLoad) && !string.IsNullOrEmpty(gpuTemp))
|
||||||
|
{
|
||||||
|
exampleMarkup += $"<text x=130 y=30 size=1>GPU: {{{gpuLoad}}}% ({{{gpuTemp}}}°C)</text>\n";
|
||||||
|
exampleMarkup += $"<bar x=130 y=35 w=120 h=6 val={{{gpuLoad}}} />\n";
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(gpuLoad))
|
||||||
|
{
|
||||||
|
exampleMarkup += $"<text x=130 y=30 size=1>GPU: {{{gpuLoad}}}%</text>\n";
|
||||||
|
exampleMarkup += $"<bar x=130 y=35 w=120 h=6 val={{{gpuLoad}}} />\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(ramUsed))
|
||||||
|
{
|
||||||
|
exampleMarkup += $"<text x=0 y=50 size=1>RAM: {{{ramUsed}}} GB</text>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
txtOledMarkup.Text = exampleMarkup;
|
||||||
|
|
||||||
|
// Update preview
|
||||||
|
if (autoUpdatePreview)
|
||||||
|
UpdatePreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FindFirstSensorOfType(HardwareType hardwareType, SensorType sensorType)
|
||||||
|
{
|
||||||
|
foreach (var hardware in computer.Hardware)
|
||||||
|
{
|
||||||
|
if (hardware.HardwareType == hardwareType)
|
||||||
|
{
|
||||||
|
foreach (var sensor in hardware.Sensors)
|
||||||
|
{
|
||||||
|
if (sensor.SensorType == sensorType && sensor.Value.HasValue)
|
||||||
|
{
|
||||||
|
return GetSensorVariableName(hardware.Name, sensor.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnPreview_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
UpdateSensorValues();
|
||||||
|
UpdatePreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void txtOledMarkup_TextChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// If auto-update is enabled, update the preview when the markup changes
|
||||||
|
if (autoUpdatePreview)
|
||||||
|
{
|
||||||
|
UpdatePreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePreview()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Clear existing elements
|
||||||
|
previewElements.Clear();
|
||||||
|
|
||||||
|
// Parse the markup into preview elements
|
||||||
|
string markup = ProcessVariablesInMarkup(txtOledMarkup.Text);
|
||||||
|
ParseMarkup(markup);
|
||||||
|
|
||||||
|
// Trigger repaint of the preview panel
|
||||||
|
if (panelOledPreview != null)
|
||||||
|
panelOledPreview.Invalidate();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Don't show errors during preview - just skip rendering
|
||||||
|
Console.WriteLine($"Preview error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ProcessVariablesInMarkup(string markup)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(markup))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
// Replace variables with actual values
|
||||||
|
foreach (var sensor in sensorValues)
|
||||||
|
{
|
||||||
|
// Look for {variable} syntax in the markup
|
||||||
|
string variablePattern = $"{{{sensor.Key}}}";
|
||||||
|
|
||||||
|
// Format value based on type (integers vs decimals)
|
||||||
|
string formattedValue;
|
||||||
|
if (Math.Abs(sensor.Value - Math.Round(sensor.Value)) < 0.01)
|
||||||
|
{
|
||||||
|
formattedValue = $"{sensor.Value:F0}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
formattedValue = $"{sensor.Value:F1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
markup = markup.Replace(variablePattern, formattedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseMarkup(string markup)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(markup))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Parse text elements - <text x=0 y=10 size=1>Hello</text>
|
||||||
|
foreach (Match match in Regex.Matches(markup, @"<text\s+x=(\d+)\s+y=(\d+)(?:\s+size=(\d+))?>([^<]*)</text>"))
|
||||||
|
{
|
||||||
|
previewElements.Add(new TextElement
|
||||||
|
{
|
||||||
|
X = int.Parse(match.Groups[1].Value),
|
||||||
|
Y = int.Parse(match.Groups[2].Value),
|
||||||
|
Size = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 1,
|
||||||
|
Text = match.Groups[4].Value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse bar elements - <bar x=0 y=20 w=100 h=8 val=75 />
|
||||||
|
foreach (Match match in Regex.Matches(markup, @"<bar\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s+val=(\d+)\s*/>"))
|
||||||
|
{
|
||||||
|
previewElements.Add(new BarElement
|
||||||
|
{
|
||||||
|
X = int.Parse(match.Groups[1].Value),
|
||||||
|
Y = int.Parse(match.Groups[2].Value),
|
||||||
|
Width = int.Parse(match.Groups[3].Value),
|
||||||
|
Height = int.Parse(match.Groups[4].Value),
|
||||||
|
Value = int.Parse(match.Groups[5].Value)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse rect elements - <rect x=0 y=0 w=20 h=10 />
|
||||||
|
foreach (Match match in Regex.Matches(markup, @"<rect\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s*/>"))
|
||||||
|
{
|
||||||
|
previewElements.Add(new RectElement
|
||||||
|
{
|
||||||
|
X = int.Parse(match.Groups[1].Value),
|
||||||
|
Y = int.Parse(match.Groups[2].Value),
|
||||||
|
Width = int.Parse(match.Groups[3].Value),
|
||||||
|
Height = int.Parse(match.Groups[4].Value),
|
||||||
|
Filled = false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse box elements - <box x=0 y=0 w=20 h=10 />
|
||||||
|
foreach (Match match in Regex.Matches(markup, @"<box\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s*/>"))
|
||||||
|
{
|
||||||
|
previewElements.Add(new RectElement
|
||||||
|
{
|
||||||
|
X = int.Parse(match.Groups[1].Value),
|
||||||
|
Y = int.Parse(match.Groups[2].Value),
|
||||||
|
Width = int.Parse(match.Groups[3].Value),
|
||||||
|
Height = int.Parse(match.Groups[4].Value),
|
||||||
|
Filled = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse line elements - <line x1=0 y1=0 x2=20 y2=20 />
|
||||||
|
foreach (Match match in Regex.Matches(markup, @"<line\s+x1=(\d+)\s+y1=(\d+)\s+x2=(\d+)\s+y2=(\d+)\s*/>"))
|
||||||
|
{
|
||||||
|
previewElements.Add(new LineElement
|
||||||
|
{
|
||||||
|
X1 = int.Parse(match.Groups[1].Value),
|
||||||
|
Y1 = int.Parse(match.Groups[2].Value),
|
||||||
|
X2 = int.Parse(match.Groups[3].Value),
|
||||||
|
Y2 = int.Parse(match.Groups[4].Value)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse icon elements - <icon x=0 y=0 name=cpu />
|
||||||
|
foreach (Match match in Regex.Matches(markup, @"<icon\s+x=(\d+)\s+y=(\d+)\s+name=([a-zA-Z0-9_]+)\s*/>"))
|
||||||
|
{
|
||||||
|
previewElements.Add(new IconElement
|
||||||
|
{
|
||||||
|
X = int.Parse(match.Groups[1].Value),
|
||||||
|
Y = int.Parse(match.Groups[2].Value),
|
||||||
|
Name = match.Groups[3].Value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void panelOledPreview_Paint(object sender, PaintEventArgs e)
|
||||||
|
{
|
||||||
|
// Create graphics object with smooth rendering
|
||||||
|
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||||||
|
|
||||||
|
// Draw OLED display boundary
|
||||||
|
int panelWidth = panelOledPreview.Width;
|
||||||
|
int panelHeight = panelOledPreview.Height;
|
||||||
|
|
||||||
|
// Calculate scale to fit preview in panel while maintaining aspect ratio
|
||||||
|
float scaleX = (float)panelWidth / OledWidth;
|
||||||
|
float scaleY = (float)panelHeight / OledHeight;
|
||||||
|
float scale = Math.Min(scaleX, scaleY);
|
||||||
|
|
||||||
|
// Calculate centered position
|
||||||
|
int displayWidth = (int)(OledWidth * scale);
|
||||||
|
int displayHeight = (int)(OledHeight * scale);
|
||||||
|
int offsetX = (panelWidth - displayWidth) / 2;
|
||||||
|
int offsetY = (panelHeight - displayHeight) / 2;
|
||||||
|
|
||||||
|
// Draw display outline
|
||||||
|
Rectangle displayRect = new Rectangle(offsetX, offsetY, displayWidth, displayHeight);
|
||||||
|
e.Graphics.DrawRectangle(Pens.DarkGray, displayRect);
|
||||||
|
|
||||||
|
// Set up transformation to scale the preview elements
|
||||||
|
e.Graphics.TranslateTransform(offsetX, offsetY);
|
||||||
|
e.Graphics.ScaleTransform(scale, scale);
|
||||||
|
|
||||||
|
// Draw all elements
|
||||||
|
foreach (var element in previewElements)
|
||||||
|
{
|
||||||
|
element.Draw(e.Graphics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset transformation
|
||||||
|
e.Graphics.ResetTransform();
|
||||||
|
|
||||||
|
// Draw labels and guidelines
|
||||||
|
e.Graphics.DrawString($"OLED: {OledWidth}x{OledHeight}", new Font("Arial", 8), Brushes.Gray, 5, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnFormClosing(e);
|
||||||
|
|
||||||
|
// Clean up resources
|
||||||
|
sensorUpdateTimer?.Stop();
|
||||||
|
sensorUpdateTimer?.Dispose();
|
||||||
|
computer?.Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
317
PCPalConfigurator/IconBrowser/IconBrowser.Designer.cs
generated
Normal file
317
PCPalConfigurator/IconBrowser/IconBrowser.Designer.cs
generated
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
namespace PCPalConfigurator
|
||||||
|
{
|
||||||
|
partial class IconBrowser
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.panelTop = new System.Windows.Forms.Panel();
|
||||||
|
this.btnBrowse = new System.Windows.Forms.Button();
|
||||||
|
this.txtIconDirectory = new System.Windows.Forms.TextBox();
|
||||||
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
|
this.panelBottom = new System.Windows.Forms.Panel();
|
||||||
|
this.txtIconDimensions = new System.Windows.Forms.TextBox();
|
||||||
|
this.label5 = new System.Windows.Forms.Label();
|
||||||
|
this.numY = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.numX = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.label4 = new System.Windows.Forms.Label();
|
||||||
|
this.label3 = new System.Windows.Forms.Label();
|
||||||
|
this.txtIconName = new System.Windows.Forms.TextBox();
|
||||||
|
this.label2 = new System.Windows.Forms.Label();
|
||||||
|
this.btnCancel = new System.Windows.Forms.Button();
|
||||||
|
this.btnInsert = new System.Windows.Forms.Button();
|
||||||
|
this.lblStatus = new System.Windows.Forms.Label();
|
||||||
|
this.splitContainer = new System.Windows.Forms.SplitContainer();
|
||||||
|
this.lstIcons = new System.Windows.Forms.ListBox();
|
||||||
|
this.flowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||||
|
this.panelTop.SuspendLayout();
|
||||||
|
this.panelBottom.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.numY)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.numX)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
|
||||||
|
this.splitContainer.Panel1.SuspendLayout();
|
||||||
|
this.splitContainer.Panel2.SuspendLayout();
|
||||||
|
this.splitContainer.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// panelTop
|
||||||
|
//
|
||||||
|
this.panelTop.Controls.Add(this.btnBrowse);
|
||||||
|
this.panelTop.Controls.Add(this.txtIconDirectory);
|
||||||
|
this.panelTop.Controls.Add(this.label1);
|
||||||
|
this.panelTop.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
|
this.panelTop.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.panelTop.Name = "panelTop";
|
||||||
|
this.panelTop.Size = new System.Drawing.Size(584, 40);
|
||||||
|
this.panelTop.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// btnBrowse
|
||||||
|
//
|
||||||
|
this.btnBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnBrowse.Location = new System.Drawing.Point(497, 9);
|
||||||
|
this.btnBrowse.Name = "btnBrowse";
|
||||||
|
this.btnBrowse.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.btnBrowse.TabIndex = 2;
|
||||||
|
this.btnBrowse.Text = "Browse...";
|
||||||
|
this.btnBrowse.UseVisualStyleBackColor = true;
|
||||||
|
this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
|
||||||
|
//
|
||||||
|
// txtIconDirectory
|
||||||
|
//
|
||||||
|
this.txtIconDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.txtIconDirectory.Location = new System.Drawing.Point(119, 10);
|
||||||
|
this.txtIconDirectory.Name = "txtIconDirectory";
|
||||||
|
this.txtIconDirectory.Size = new System.Drawing.Size(372, 23);
|
||||||
|
this.txtIconDirectory.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// label1
|
||||||
|
//
|
||||||
|
this.label1.AutoSize = true;
|
||||||
|
this.label1.Location = new System.Drawing.Point(12, 13);
|
||||||
|
this.label1.Name = "label1";
|
||||||
|
this.label1.Size = new System.Drawing.Size(101, 15);
|
||||||
|
this.label1.TabIndex = 0;
|
||||||
|
this.label1.Text = "Icons Directory:";
|
||||||
|
//
|
||||||
|
// panelBottom
|
||||||
|
//
|
||||||
|
this.panelBottom.Controls.Add(this.txtIconDimensions);
|
||||||
|
this.panelBottom.Controls.Add(this.label5);
|
||||||
|
this.panelBottom.Controls.Add(this.numY);
|
||||||
|
this.panelBottom.Controls.Add(this.numX);
|
||||||
|
this.panelBottom.Controls.Add(this.label4);
|
||||||
|
this.panelBottom.Controls.Add(this.label3);
|
||||||
|
this.panelBottom.Controls.Add(this.txtIconName);
|
||||||
|
this.panelBottom.Controls.Add(this.label2);
|
||||||
|
this.panelBottom.Controls.Add(this.btnCancel);
|
||||||
|
this.panelBottom.Controls.Add(this.btnInsert);
|
||||||
|
this.panelBottom.Controls.Add(this.lblStatus);
|
||||||
|
this.panelBottom.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
|
this.panelBottom.Location = new System.Drawing.Point(0, 341);
|
||||||
|
this.panelBottom.Name = "panelBottom";
|
||||||
|
this.panelBottom.Size = new System.Drawing.Size(584, 120);
|
||||||
|
this.panelBottom.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// txtIconDimensions
|
||||||
|
//
|
||||||
|
this.txtIconDimensions.Location = new System.Drawing.Point(119, 39);
|
||||||
|
this.txtIconDimensions.Name = "txtIconDimensions";
|
||||||
|
this.txtIconDimensions.ReadOnly = true;
|
||||||
|
this.txtIconDimensions.Size = new System.Drawing.Size(100, 23);
|
||||||
|
this.txtIconDimensions.TabIndex = 10;
|
||||||
|
//
|
||||||
|
// label5
|
||||||
|
//
|
||||||
|
this.label5.AutoSize = true;
|
||||||
|
this.label5.Location = new System.Drawing.Point(12, 42);
|
||||||
|
this.label5.Name = "label5";
|
||||||
|
this.label5.Size = new System.Drawing.Size(77, 15);
|
||||||
|
this.label5.TabIndex = 9;
|
||||||
|
this.label5.Text = "Dimensions:";
|
||||||
|
//
|
||||||
|
// numY
|
||||||
|
//
|
||||||
|
this.numY.Location = new System.Drawing.Point(119, 82);
|
||||||
|
this.numY.Maximum = new decimal(new int[] {
|
||||||
|
200,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.numY.Name = "numY";
|
||||||
|
this.numY.Size = new System.Drawing.Size(60, 23);
|
||||||
|
this.numY.TabIndex = 8;
|
||||||
|
//
|
||||||
|
// numX
|
||||||
|
//
|
||||||
|
this.numX.Location = new System.Drawing.Point(119, 68);
|
||||||
|
this.numX.Maximum = new decimal(new int[] {
|
||||||
|
200,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.numX.Name = "numX";
|
||||||
|
this.numX.Size = new System.Drawing.Size(60, 23);
|
||||||
|
this.numX.TabIndex = 7;
|
||||||
|
//
|
||||||
|
// label4
|
||||||
|
//
|
||||||
|
this.label4.AutoSize = true;
|
||||||
|
this.label4.Location = new System.Drawing.Point(12, 84);
|
||||||
|
this.label4.Name = "label4";
|
||||||
|
this.label4.Size = new System.Drawing.Size(67, 15);
|
||||||
|
this.label4.TabIndex = 6;
|
||||||
|
this.label4.Text = "Y Position:";
|
||||||
|
//
|
||||||
|
// label3
|
||||||
|
//
|
||||||
|
this.label3.AutoSize = true;
|
||||||
|
this.label3.Location = new System.Drawing.Point(12, 70);
|
||||||
|
this.label3.Name = "label3";
|
||||||
|
this.label3.Size = new System.Drawing.Size(67, 15);
|
||||||
|
this.label3.TabIndex = 5;
|
||||||
|
this.label3.Text = "X Position:";
|
||||||
|
//
|
||||||
|
// txtIconName
|
||||||
|
//
|
||||||
|
this.txtIconName.Location = new System.Drawing.Point(119, 10);
|
||||||
|
this.txtIconName.Name = "txtIconName";
|
||||||
|
this.txtIconName.ReadOnly = true;
|
||||||
|
this.txtIconName.Size = new System.Drawing.Size(200, 23);
|
||||||
|
this.txtIconName.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// label2
|
||||||
|
//
|
||||||
|
this.label2.AutoSize = true;
|
||||||
|
this.label2.Location = new System.Drawing.Point(12, 13);
|
||||||
|
this.label2.Name = "label2";
|
||||||
|
this.label2.Size = new System.Drawing.Size(101, 15);
|
||||||
|
this.label2.TabIndex = 3;
|
||||||
|
this.label2.Text = "Selected Icon:";
|
||||||
|
//
|
||||||
|
// btnCancel
|
||||||
|
//
|
||||||
|
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||||
|
this.btnCancel.Location = new System.Drawing.Point(497, 85);
|
||||||
|
this.btnCancel.Name = "btnCancel";
|
||||||
|
this.btnCancel.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.btnCancel.TabIndex = 2;
|
||||||
|
this.btnCancel.Text = "Cancel";
|
||||||
|
this.btnCancel.UseVisualStyleBackColor = true;
|
||||||
|
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||||
|
//
|
||||||
|
// btnInsert
|
||||||
|
//
|
||||||
|
this.btnInsert.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnInsert.Location = new System.Drawing.Point(416, 85);
|
||||||
|
this.btnInsert.Name = "btnInsert";
|
||||||
|
this.btnInsert.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.btnInsert.TabIndex = 1;
|
||||||
|
this.btnInsert.Text = "Insert";
|
||||||
|
this.btnInsert.UseVisualStyleBackColor = true;
|
||||||
|
this.btnInsert.Click += new System.EventHandler(this.btnInsert_Click);
|
||||||
|
//
|
||||||
|
// lblStatus
|
||||||
|
//
|
||||||
|
this.lblStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.lblStatus.AutoSize = true;
|
||||||
|
this.lblStatus.Location = new System.Drawing.Point(325, 13);
|
||||||
|
this.lblStatus.Name = "lblStatus";
|
||||||
|
this.lblStatus.Size = new System.Drawing.Size(0, 15);
|
||||||
|
this.lblStatus.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// splitContainer
|
||||||
|
//
|
||||||
|
this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.splitContainer.Location = new System.Drawing.Point(0, 40);
|
||||||
|
this.splitContainer.Name = "splitContainer";
|
||||||
|
//
|
||||||
|
// splitContainer.Panel1
|
||||||
|
//
|
||||||
|
this.splitContainer.Panel1.Controls.Add(this.lstIcons);
|
||||||
|
this.splitContainer.Panel1Collapsed = true;
|
||||||
|
this.splitContainer.Panel1MinSize = 0;
|
||||||
|
//
|
||||||
|
// splitContainer.Panel2
|
||||||
|
//
|
||||||
|
this.splitContainer.Panel2.Controls.Add(this.flowLayoutPanel);
|
||||||
|
this.splitContainer.Size = new System.Drawing.Size(584, 301);
|
||||||
|
this.splitContainer.SplitterDistance = 194;
|
||||||
|
this.splitContainer.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// lstIcons
|
||||||
|
//
|
||||||
|
this.lstIcons.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.lstIcons.FormattingEnabled = true;
|
||||||
|
this.lstIcons.ItemHeight = 15;
|
||||||
|
this.lstIcons.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.lstIcons.Name = "lstIcons";
|
||||||
|
this.lstIcons.Size = new System.Drawing.Size(194, 301);
|
||||||
|
this.lstIcons.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// flowLayoutPanel
|
||||||
|
//
|
||||||
|
this.flowLayoutPanel.AutoScroll = true;
|
||||||
|
this.flowLayoutPanel.BackColor = System.Drawing.SystemColors.ControlLightLight;
|
||||||
|
this.flowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.flowLayoutPanel.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.flowLayoutPanel.Name = "flowLayoutPanel";
|
||||||
|
this.flowLayoutPanel.Padding = new System.Windows.Forms.Padding(10);
|
||||||
|
this.flowLayoutPanel.Size = new System.Drawing.Size(584, 301);
|
||||||
|
this.flowLayoutPanel.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// IconBrowser
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(584, 461);
|
||||||
|
this.Controls.Add(this.splitContainer);
|
||||||
|
this.Controls.Add(this.panelBottom);
|
||||||
|
this.Controls.Add(this.panelTop);
|
||||||
|
this.MinimizeBox = false;
|
||||||
|
this.MinimumSize = new System.Drawing.Size(400, 400);
|
||||||
|
this.Name = "IconBrowser";
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.Text = "Icon Browser";
|
||||||
|
this.Load += new System.EventHandler(this.IconBrowser_Load);
|
||||||
|
this.panelTop.ResumeLayout(false);
|
||||||
|
this.panelTop.PerformLayout();
|
||||||
|
this.panelBottom.ResumeLayout(false);
|
||||||
|
this.panelBottom.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.numY)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.numX)).EndInit();
|
||||||
|
this.splitContainer.Panel1.ResumeLayout(false);
|
||||||
|
this.splitContainer.Panel2.ResumeLayout(false);
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
|
||||||
|
this.splitContainer.ResumeLayout(false);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private Panel panelTop;
|
||||||
|
private Button btnBrowse;
|
||||||
|
private TextBox txtIconDirectory;
|
||||||
|
private Label label1;
|
||||||
|
private Panel panelBottom;
|
||||||
|
private Button btnCancel;
|
||||||
|
private Button btnInsert;
|
||||||
|
private Label lblStatus;
|
||||||
|
private SplitContainer splitContainer;
|
||||||
|
private ListBox lstIcons;
|
||||||
|
private FlowLayoutPanel flowLayoutPanel;
|
||||||
|
private TextBox txtIconName;
|
||||||
|
private Label label2;
|
||||||
|
private NumericUpDown numY;
|
||||||
|
private NumericUpDown numX;
|
||||||
|
private Label label4;
|
||||||
|
private Label label3;
|
||||||
|
private TextBox txtIconDimensions;
|
||||||
|
private Label label5;
|
||||||
|
}
|
||||||
|
}
|
||||||
215
PCPalConfigurator/IconBrowser/IconBrowser.cs
Normal file
215
PCPalConfigurator/IconBrowser/IconBrowser.cs
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace PCPalConfigurator
|
||||||
|
{
|
||||||
|
public partial class IconBrowser : Form
|
||||||
|
{
|
||||||
|
private string _selectedIconPath = "";
|
||||||
|
private int _selectedIconWidth = 0;
|
||||||
|
private int _selectedIconHeight = 0;
|
||||||
|
public string SelectedIconName { get; private set; } = "";
|
||||||
|
|
||||||
|
public IconBrowser()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IconBrowser_Load(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Load last used directory from settings if available
|
||||||
|
string lastDir = Properties.Settings.Default.LastIconDirectory;
|
||||||
|
if (!string.IsNullOrEmpty(lastDir) && Directory.Exists(lastDir))
|
||||||
|
{
|
||||||
|
txtIconDirectory.Text = lastDir;
|
||||||
|
LoadIconsFromDirectory(lastDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnBrowse_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using FolderBrowserDialog dialog = new();
|
||||||
|
if (!string.IsNullOrEmpty(txtIconDirectory.Text) && Directory.Exists(txtIconDirectory.Text))
|
||||||
|
{
|
||||||
|
dialog.InitialDirectory = txtIconDirectory.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
txtIconDirectory.Text = dialog.SelectedPath;
|
||||||
|
LoadIconsFromDirectory(dialog.SelectedPath);
|
||||||
|
|
||||||
|
// Save the directory for next time
|
||||||
|
Properties.Settings.Default.LastIconDirectory = dialog.SelectedPath;
|
||||||
|
Properties.Settings.Default.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadIconsFromDirectory(string directory)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lstIcons.Items.Clear();
|
||||||
|
flowLayoutPanel.Controls.Clear();
|
||||||
|
|
||||||
|
string[] xbmFiles = Directory.GetFiles(directory, "*.bin");
|
||||||
|
if (xbmFiles.Length == 0)
|
||||||
|
{
|
||||||
|
MessageBox.Show("No XBM icon files found in the selected directory.",
|
||||||
|
"No Icons Found", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string file in xbmFiles)
|
||||||
|
{
|
||||||
|
// Create a button for each icon
|
||||||
|
Button iconButton = new();
|
||||||
|
|
||||||
|
// Parse the XBM file to get dimensions
|
||||||
|
(int width, int height) = ParseXbmDimensions(file);
|
||||||
|
|
||||||
|
string fileName = Path.GetFileNameWithoutExtension(file);
|
||||||
|
iconButton.Text = $"{fileName} ({width}x{height})";
|
||||||
|
iconButton.Tag = new IconInfo
|
||||||
|
{
|
||||||
|
Path = file,
|
||||||
|
Name = fileName,
|
||||||
|
Width = width,
|
||||||
|
Height = height
|
||||||
|
};
|
||||||
|
|
||||||
|
iconButton.Size = new Size(150, 40);
|
||||||
|
iconButton.Click += IconButton_Click;
|
||||||
|
flowLayoutPanel.Controls.Add(iconButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
lblStatus.Text = $"Found {xbmFiles.Length} XBM files";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error loading icons: {ex.Message}",
|
||||||
|
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private (int width, int height) ParseXbmDimensions(string filePath)
|
||||||
|
{
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Read the first few lines of the XBM file
|
||||||
|
using StreamReader reader = new(filePath);
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null && (width == 0 || height == 0))
|
||||||
|
{
|
||||||
|
// Look for width and height definitions in XBM format
|
||||||
|
if (line.Contains("_width"))
|
||||||
|
{
|
||||||
|
width = ExtractNumberFromLine(line);
|
||||||
|
}
|
||||||
|
else if (line.Contains("_height"))
|
||||||
|
{
|
||||||
|
height = ExtractNumberFromLine(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If we can't parse, default to unknown dimensions
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ExtractNumberFromLine(string line)
|
||||||
|
{
|
||||||
|
// Extract the numeric value from a line like "#define icon_width 16"
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string[] parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
{
|
||||||
|
if (int.TryParse(parts[2], out int result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IconButton_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Button button && button.Tag is IconInfo info)
|
||||||
|
{
|
||||||
|
// Highlight the selected button
|
||||||
|
foreach (Control control in flowLayoutPanel.Controls)
|
||||||
|
{
|
||||||
|
if (control is Button btn)
|
||||||
|
{
|
||||||
|
btn.BackColor = SystemColors.Control;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.BackColor = Color.LightBlue;
|
||||||
|
|
||||||
|
// Store the selected icon info
|
||||||
|
SelectedIconName = info.Name;
|
||||||
|
_selectedIconPath = info.Path;
|
||||||
|
_selectedIconWidth = info.Width;
|
||||||
|
_selectedIconHeight = info.Height;
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
txtIconName.Text = info.Name;
|
||||||
|
txtIconDimensions.Text = $"{info.Width} x {info.Height}";
|
||||||
|
numX.Value = 0;
|
||||||
|
numY.Value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnInsert_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(SelectedIconName))
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please select an icon first.",
|
||||||
|
"No Icon Selected", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark as success and close
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnCancel_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = DialogResult.Cancel;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetIconMarkup()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(SelectedIconName))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"<icon x={numX.Value} y={numY.Value} name={SelectedIconName} />";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper class for icon information
|
||||||
|
private class IconInfo
|
||||||
|
{
|
||||||
|
public string Path { get; set; } = "";
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public int Width { get; set; }
|
||||||
|
public int Height { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
PCPalConfigurator/IconBrowser/IconBrowser.resx
Normal file
120
PCPalConfigurator/IconBrowser/IconBrowser.resx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
@@ -13,4 +13,25 @@
|
|||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Properties\Settings.Designer.cs">
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Core\" />
|
||||||
|
<Folder Include="Rendering\Elements\" />
|
||||||
|
<Folder Include="UI\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -4,5 +4,8 @@
|
|||||||
<Compile Update="ConfiguratorForm.cs">
|
<Compile Update="ConfiguratorForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Update="IconBrowser\IconBrowser.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
38
PCPalConfigurator/Properties/Settings.Designer.cs
generated
Normal file
38
PCPalConfigurator/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace PCPalConfigurator.Properties {
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default {
|
||||||
|
get {
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||||
|
public string LastIconDirectory {
|
||||||
|
get {
|
||||||
|
return ((string)(this["LastIconDirectory"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["LastIconDirectory"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
PCPalConfigurator/Properties/Settings.settings
Normal file
9
PCPalConfigurator/Properties/Settings.settings
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="PCPalConfigurator" GeneratedClassName="Settings">
|
||||||
|
<Profiles />
|
||||||
|
<Settings>
|
||||||
|
<Setting Name="LastIconDirectory" Type="System.String" Scope="User">
|
||||||
|
<Value Profile="(Default)" />
|
||||||
|
</Setting>
|
||||||
|
</Settings>
|
||||||
|
</SettingsFile>
|
||||||
@@ -6,6 +6,7 @@ using System.IO.Ports;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using LibreHardwareMonitor.Hardware;
|
using LibreHardwareMonitor.Hardware;
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ namespace PCPalService
|
|||||||
private readonly ILogger<Worker> _logger;
|
private readonly ILogger<Worker> _logger;
|
||||||
private SerialPort serialPort;
|
private SerialPort serialPort;
|
||||||
private Computer computer;
|
private Computer computer;
|
||||||
|
private readonly Dictionary<string, float> _sensorValues = new();
|
||||||
|
|
||||||
private const string AppDataFolder = "PCPal";
|
private const string AppDataFolder = "PCPal";
|
||||||
private const string ConfigFileName = "config.json";
|
private const string ConfigFileName = "config.json";
|
||||||
@@ -33,7 +35,9 @@ namespace PCPalService
|
|||||||
IsCpuEnabled = true,
|
IsCpuEnabled = true,
|
||||||
IsMemoryEnabled = true,
|
IsMemoryEnabled = true,
|
||||||
IsGpuEnabled = true,
|
IsGpuEnabled = true,
|
||||||
IsMotherboardEnabled = true
|
IsMotherboardEnabled = true,
|
||||||
|
IsControllerEnabled = true,
|
||||||
|
IsStorageEnabled = true
|
||||||
};
|
};
|
||||||
computer.Open();
|
computer.Open();
|
||||||
|
|
||||||
@@ -58,33 +62,93 @@ namespace PCPalService
|
|||||||
{
|
{
|
||||||
serialPort = new SerialPort(port, 115200);
|
serialPort = new SerialPort(port, 115200);
|
||||||
serialPort.Open();
|
serialPort.Open();
|
||||||
_logger.LogInformation($"Connected to ESP32 on {port}");
|
_logger.LogInformation($"Connected to display device on {port}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogWarning("ESP32 not found. Retrying in 5 seconds...");
|
_logger.LogWarning("Display device not found. Retrying in 5 seconds...");
|
||||||
await Task.Delay(5000, stoppingToken);
|
await Task.Delay(5000, stoppingToken);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string line1 = GetSensorLine(config.Line1Selection, config.Line1CustomText, config.Line1PostText);
|
// Update sensor values
|
||||||
string line2 = GetSensorLine(config.Line2Selection, config.Line2CustomText, config.Line2PostText);
|
UpdateSensorValues();
|
||||||
|
|
||||||
serialPort.WriteLine($"CMD:LCD,0,{line1}");
|
// Send appropriate commands based on screen type
|
||||||
serialPort.WriteLine($"CMD:LCD,1,{line2}");
|
if (config.ScreenType == "1602" || config.ScreenType == "TFT4_6")
|
||||||
_logger.LogInformation("Data sent to LCD.");
|
{
|
||||||
|
// LCD display handling
|
||||||
|
string line1 = GetSensorLine(config.Line1Selection, config.Line1CustomText, config.Line1PostText);
|
||||||
|
string line2 = GetSensorLine(config.Line2Selection, config.Line2CustomText, config.Line2PostText);
|
||||||
|
|
||||||
|
serialPort.WriteLine($"CMD:LCD,0,{line1}");
|
||||||
|
serialPort.WriteLine($"CMD:LCD,1,{line2}");
|
||||||
|
_logger.LogDebug("Data sent to LCD.");
|
||||||
|
}
|
||||||
|
else if (config.ScreenType == "OLED")
|
||||||
|
{
|
||||||
|
// OLED display handling
|
||||||
|
string processedMarkup = ProcessVariablesInMarkup(config.OledMarkup);
|
||||||
|
serialPort.WriteLine($"CMD:OLED,{processedMarkup}");
|
||||||
|
_logger.LogDebug("Data sent to OLED.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning($"Unknown screen type: {config.ScreenType}");
|
||||||
|
}
|
||||||
|
|
||||||
await Task.Delay(5000, stoppingToken);
|
await Task.Delay(5000, stoppingToken);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error in main loop");
|
_logger.LogError(ex, "Error in main loop");
|
||||||
|
DisconnectFromDevice();
|
||||||
await Task.Delay(5000, stoppingToken);
|
await Task.Delay(5000, stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serialPort?.Close();
|
DisconnectFromDevice();
|
||||||
|
computer.Close();
|
||||||
|
_logger.LogInformation("Service shutting down...");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisconnectFromDevice()
|
||||||
|
{
|
||||||
|
if (serialPort != null && serialPort.IsOpen)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
serialPort.Close();
|
||||||
|
serialPort.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error disconnecting from device");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
serialPort = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSensorValues()
|
||||||
|
{
|
||||||
|
// Update all hardware readings
|
||||||
|
foreach (var hardware in computer.Hardware)
|
||||||
|
{
|
||||||
|
hardware.Update();
|
||||||
|
|
||||||
|
foreach (var sensor in hardware.Sensors)
|
||||||
|
{
|
||||||
|
if (sensor.Value.HasValue)
|
||||||
|
{
|
||||||
|
string sensorId = GetSensorVariableName(hardware.Name, sensor.Name);
|
||||||
|
_sensorValues[sensorId] = sensor.Value.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetConfigPath()
|
private string GetConfigPath()
|
||||||
@@ -109,11 +173,15 @@ namespace PCPalService
|
|||||||
using (SerialPort test = new SerialPort(port, 115200))
|
using (SerialPort test = new SerialPort(port, 115200))
|
||||||
{
|
{
|
||||||
test.Open();
|
test.Open();
|
||||||
test.WriteLine("CMD:GET_LCD_TYPE");
|
test.WriteLine("CMD:GET_DISPLAY_TYPE");
|
||||||
Thread.Sleep(500);
|
Thread.Sleep(500);
|
||||||
string response = test.ReadExisting();
|
string response = test.ReadExisting();
|
||||||
if (response.Contains("LCD_TYPE:1602A"))
|
|
||||||
|
if (response.Contains("DISPLAY_TYPE:1602A") ||
|
||||||
|
response.Contains("DISPLAY_TYPE:OLED"))
|
||||||
|
{
|
||||||
return port;
|
return port;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
@@ -123,15 +191,15 @@ namespace PCPalService
|
|||||||
|
|
||||||
private string GetSensorLine(string selection, string prefix, string suffix)
|
private string GetSensorLine(string selection, string prefix, string suffix)
|
||||||
{
|
{
|
||||||
if (selection == "Custom Text")
|
if (selection == "Custom Text" || string.IsNullOrEmpty(selection))
|
||||||
return prefix;
|
return prefix ?? string.Empty;
|
||||||
|
|
||||||
var parsed = ParseSensorSelection(selection);
|
var parsed = ParseSensorSelection(selection);
|
||||||
if (parsed == null)
|
if (parsed == null)
|
||||||
return "N/A";
|
return "N/A";
|
||||||
|
|
||||||
string value = GetSensorValue(parsed.Value.hardwareType, parsed.Value.sensorName, parsed.Value.sensorType);
|
string value = GetSensorValue(parsed.Value.hardwareType, parsed.Value.sensorName, parsed.Value.sensorType);
|
||||||
return $"{prefix}{value}{suffix}";
|
return $"{prefix ?? ""}{value}{suffix ?? ""}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private (HardwareType hardwareType, string sensorName, SensorType sensorType)? ParseSensorSelection(string input)
|
private (HardwareType hardwareType, string sensorName, SensorType sensorType)? ParseSensorSelection(string input)
|
||||||
@@ -167,7 +235,6 @@ namespace PCPalService
|
|||||||
{
|
{
|
||||||
if (hardware.HardwareType == type)
|
if (hardware.HardwareType == type)
|
||||||
{
|
{
|
||||||
hardware.Update();
|
|
||||||
foreach (var sensor in hardware.Sensors)
|
foreach (var sensor in hardware.Sensors)
|
||||||
{
|
{
|
||||||
if (sensor.SensorType == sensorType && sensor.Name == name)
|
if (sensor.SensorType == sensorType && sensor.Name == name)
|
||||||
@@ -179,16 +246,66 @@ namespace PCPalService
|
|||||||
}
|
}
|
||||||
return "N/A";
|
return "N/A";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ProcessVariablesInMarkup(string markup)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(markup))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
// Replace variables with actual values
|
||||||
|
foreach (var sensor in _sensorValues)
|
||||||
|
{
|
||||||
|
// Look for {variable} syntax in the markup
|
||||||
|
string variablePattern = $"{{{sensor.Key}}}";
|
||||||
|
|
||||||
|
// Format value based on type (integers vs decimals)
|
||||||
|
string formattedValue;
|
||||||
|
if (Math.Abs(sensor.Value - Math.Round(sensor.Value)) < 0.01)
|
||||||
|
{
|
||||||
|
formattedValue = $"{sensor.Value:F0}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
formattedValue = $"{sensor.Value:F1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
markup = markup.Replace(variablePattern, formattedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSensorVariableName(string hardwareName, string sensorName)
|
||||||
|
{
|
||||||
|
// Create a simplified and safe variable name
|
||||||
|
string name = $"{hardwareName}_{sensorName}"
|
||||||
|
.Replace(" ", "_")
|
||||||
|
.Replace("%", "Percent")
|
||||||
|
.Replace("#", "Num")
|
||||||
|
.Replace("/", "_")
|
||||||
|
.Replace("\\", "_")
|
||||||
|
.Replace("(", "")
|
||||||
|
.Replace(")", "")
|
||||||
|
.Replace(",", "");
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConfigData
|
public class ConfigData
|
||||||
{
|
{
|
||||||
|
// Existing properties
|
||||||
|
public string LastUsedPort { get; set; }
|
||||||
|
public string ScreenType { get; set; } // Could be "1602", "TFT4_6", or "OLED"
|
||||||
public string Line1Selection { get; set; }
|
public string Line1Selection { get; set; }
|
||||||
public string Line1CustomText { get; set; }
|
public string Line1CustomText { get; set; }
|
||||||
public string Line1PostText { get; set; }
|
|
||||||
public string Line2Selection { get; set; }
|
public string Line2Selection { get; set; }
|
||||||
public string Line2CustomText { get; set; }
|
public string Line2CustomText { get; set; }
|
||||||
|
public string Line1PostText { get; set; }
|
||||||
public string Line2PostText { get; set; }
|
public string Line2PostText { get; set; }
|
||||||
public string ScreenType { get; set; }
|
|
||||||
|
// New properties for OLED support
|
||||||
|
public string OledMarkup { get; set; } // Store the full markup for OLED
|
||||||
|
public string LastIconDirectory { get; set; } // Remember last used icon directory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
170
RGB666_tests/RGB666_tests.ino
Normal file
170
RGB666_tests/RGB666_tests.ino
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Limor Fried for Adafruit Industries
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <Arduino_GFX_Library.h>
|
||||||
|
#include <Adafruit_FT6206.h>
|
||||||
|
#include <Adafruit_CST8XX.h>
|
||||||
|
|
||||||
|
Arduino_XCA9554SWSPI *expander = new Arduino_XCA9554SWSPI(
|
||||||
|
PCA_TFT_RESET, PCA_TFT_CS, PCA_TFT_SCK, PCA_TFT_MOSI,
|
||||||
|
&Wire, 0x3F);
|
||||||
|
|
||||||
|
Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
|
||||||
|
TFT_DE, TFT_VSYNC, TFT_HSYNC, TFT_PCLK,
|
||||||
|
TFT_R1, TFT_R2, TFT_R3, TFT_R4, TFT_R5,
|
||||||
|
TFT_G0, TFT_G1, TFT_G2, TFT_G3, TFT_G4, TFT_G5,
|
||||||
|
TFT_B1, TFT_B2, TFT_B3, TFT_B4, TFT_B5,
|
||||||
|
1 /* hsync_polarity */, 50 /* hsync_front_porch */, 2 /* hsync_pulse_width */, 44 /* hsync_back_porch */,
|
||||||
|
1 /* vsync_polarity */, 16 /* vsync_front_porch */, 2 /* vsync_pulse_width */, 18 /* vsync_back_porch */
|
||||||
|
// ,1, 30000000
|
||||||
|
);
|
||||||
|
|
||||||
|
Arduino_RGB_Display *gfx = new Arduino_RGB_Display(
|
||||||
|
// 2.1" 480x480 round display
|
||||||
|
// 480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||||
|
// expander, GFX_NOT_DEFINED /* RST */, TL021WVC02_init_operations, sizeof(TL021WVC02_init_operations));
|
||||||
|
|
||||||
|
// 2.8" 480x480 round display
|
||||||
|
// 480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||||
|
// expander, GFX_NOT_DEFINED /* RST */, TL028WVC01_init_operations, sizeof(TL028WVC01_init_operations));
|
||||||
|
|
||||||
|
// 3.4" 480x480 square display
|
||||||
|
// 480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||||
|
// expander, GFX_NOT_DEFINED /* RST */, tl034wvs05_b1477a_init_operations, sizeof(tl034wvs05_b1477a_init_operations));
|
||||||
|
|
||||||
|
// 3.2" 320x820 rectangle bar display
|
||||||
|
// 320 /* width */, 820 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||||
|
// expander, GFX_NOT_DEFINED /* RST */, tl032fwv01_init_operations, sizeof(tl032fwv01_init_operations));
|
||||||
|
|
||||||
|
// 3.7" 240x960 rectangle bar display
|
||||||
|
240 /* width */, 960 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||||
|
expander, GFX_NOT_DEFINED /* RST */, HD371001C40_init_operations, sizeof(HD371001C40_init_operations), 120 /* col_offset1 */);
|
||||||
|
|
||||||
|
// 4.0" 720x720 square display
|
||||||
|
// 720 /* width */, 720 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||||
|
// expander, GFX_NOT_DEFINED /* RST */, NULL, 0);
|
||||||
|
|
||||||
|
// 4.0" 720x720 round display
|
||||||
|
// 720 /* width */, 720 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||||
|
// expander, GFX_NOT_DEFINED /* RST */, hd40015c40_init_operations, sizeof(hd40015c40_init_operations));
|
||||||
|
// needs also the rgbpanel to have these pulse/sync values:
|
||||||
|
// 1 /* hync_polarity */, 46 /* hsync_front_porch */, 2 /* hsync_pulse_width */, 44 /* hsync_back_porch */,
|
||||||
|
// 1 /* vsync_polarity */, 50 /* vsync_front_porch */, 16 /* vsync_pulse_width */, 16 /* vsync_back_porch */
|
||||||
|
|
||||||
|
uint16_t *colorWheel;
|
||||||
|
|
||||||
|
// The Capacitive touchscreen overlays uses hardware I2C (SCL/SDA)
|
||||||
|
|
||||||
|
// Most touchscreens use FocalTouch with I2C Address often but not always 0x48!
|
||||||
|
#define I2C_TOUCH_ADDR 0x48
|
||||||
|
|
||||||
|
// 2.1" 480x480 round display use CST826 touchscreen with I2C Address at 0x15
|
||||||
|
//#define I2C_TOUCH_ADDR 0x15 // often but not always 0x48!
|
||||||
|
|
||||||
|
Adafruit_FT6206 focal_ctp = Adafruit_FT6206(); // this library also supports FT5336U!
|
||||||
|
Adafruit_CST8XX cst_ctp = Adafruit_CST8XX();
|
||||||
|
bool touchOK = false; // we will check if the touchscreen exists
|
||||||
|
bool isFocalTouch = false;
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
//while (!Serial) delay(100);
|
||||||
|
|
||||||
|
#ifdef GFX_EXTRA_PRE_INIT
|
||||||
|
GFX_EXTRA_PRE_INIT();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Serial.println("Beginning");
|
||||||
|
// Init Display
|
||||||
|
|
||||||
|
Wire.setClock(1000000); // speed up I2C
|
||||||
|
if (!gfx->begin()) {
|
||||||
|
Serial.println("gfx->begin() failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Initialized!");
|
||||||
|
|
||||||
|
gfx->fillScreen(BLACK);
|
||||||
|
|
||||||
|
expander->pinMode(PCA_TFT_BACKLIGHT, OUTPUT);
|
||||||
|
expander->digitalWrite(PCA_TFT_BACKLIGHT, HIGH);
|
||||||
|
|
||||||
|
colorWheel = (uint16_t *) ps_malloc(gfx->width() * gfx->height() * sizeof(uint16_t));
|
||||||
|
if (colorWheel) {
|
||||||
|
generateColorWheel(colorWheel);
|
||||||
|
gfx->draw16bitRGBBitmap(0, 0, colorWheel, gfx->width(), gfx->height());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!focal_ctp.begin(0, &Wire, I2C_TOUCH_ADDR)) {
|
||||||
|
// Try the CST826 Touch Screen
|
||||||
|
if (!cst_ctp.begin(&Wire, I2C_TOUCH_ADDR)) {
|
||||||
|
Serial.print("No Touchscreen found at address 0x");
|
||||||
|
Serial.println(I2C_TOUCH_ADDR, HEX);
|
||||||
|
touchOK = false;
|
||||||
|
} else {
|
||||||
|
Serial.println("CST826 Touchscreen found");
|
||||||
|
touchOK = true;
|
||||||
|
isFocalTouch = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Serial.println("Focal Touchscreen found");
|
||||||
|
touchOK = true;
|
||||||
|
isFocalTouch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (touchOK) {
|
||||||
|
if (isFocalTouch && focal_ctp.touched()) {
|
||||||
|
TS_Point p = focal_ctp.getPoint(0);
|
||||||
|
Serial.printf("(%d, %d)\n", p.x, p.y);
|
||||||
|
gfx->fillRect(p.x, p.y, 5, 5, WHITE);
|
||||||
|
} else if (!isFocalTouch && cst_ctp.touched()) {
|
||||||
|
CST_TS_Point p = cst_ctp.getPoint(0);
|
||||||
|
Serial.printf("(%d, %d)\n", p.x, p.y);
|
||||||
|
gfx->fillRect(p.x, p.y, 5, 5, WHITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the buttons to turn off
|
||||||
|
if (! expander->digitalRead(PCA_BUTTON_DOWN)) {
|
||||||
|
expander->digitalWrite(PCA_TFT_BACKLIGHT, LOW);
|
||||||
|
}
|
||||||
|
// and on the backlight
|
||||||
|
if (! expander->digitalRead(PCA_BUTTON_UP)) {
|
||||||
|
expander->digitalWrite(PCA_TFT_BACKLIGHT, HIGH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://chat.openai.com/share/8edee522-7875-444f-9fea-ae93a8dfa4ec
|
||||||
|
void generateColorWheel(uint16_t *colorWheel) {
|
||||||
|
int width = gfx->width();
|
||||||
|
int height = gfx->height();
|
||||||
|
int half_width = width / 2;
|
||||||
|
int half_height = height / 2;
|
||||||
|
float angle;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
int index, scaled_index;
|
||||||
|
|
||||||
|
for(int y = 0; y < half_height; y++) {
|
||||||
|
for(int x = 0; x < half_width; x++) {
|
||||||
|
index = y * half_width + x;
|
||||||
|
angle = atan2(y - half_height / 2, x - half_width / 2);
|
||||||
|
r = uint8_t(127.5 * (cos(angle) + 1));
|
||||||
|
g = uint8_t(127.5 * (sin(angle) + 1));
|
||||||
|
b = uint8_t(255 - (r + g) / 2);
|
||||||
|
uint16_t color = RGB565(r, g, b);
|
||||||
|
|
||||||
|
// Scale this pixel into 4 pixels in the full buffer
|
||||||
|
for(int dy = 0; dy < 2; dy++) {
|
||||||
|
for(int dx = 0; dx < 2; dx++) {
|
||||||
|
scaled_index = (y * 2 + dy) * width + (x * 2 + dx);
|
||||||
|
colorWheel[scaled_index] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user