Tried using Control instead of Label as base class for our Marquee.
Sticking to Label for now.
2 using System.Collections.Generic;
3 using System.ComponentModel;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
11 using CodeProject.Dialog;
12 using System.Drawing.Imaging;
13 using System.ServiceModel;
14 using System.Threading;
15 using System.Diagnostics;
17 using SharpDisplayInterface;
18 using SharpDisplayClient;
21 namespace SharpDisplayManager
23 public partial class MainForm : Form
25 DateTime LastTickTime;
27 System.Drawing.Bitmap iBmp;
28 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
29 ServiceHost iServiceHost;
31 /// Our collection of clients
33 public Dictionary<string, ClientData> iClients;
38 LastTickTime = DateTime.Now;
39 iDisplay = new Display();
40 iClients = new Dictionary<string, ClientData>();
42 InitializeComponent();
46 marqueeLabelTop.Font = Properties.Settings.Default.DisplayFont;
47 marqueeLabelBottom.Font = Properties.Settings.Default.DisplayFont;
48 checkBoxShowBorders.Checked = Properties.Settings.Default.DisplayShowBorders;
49 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
50 checkBoxReverseScreen.Checked = Properties.Settings.Default.DisplayReverseScreen;
52 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
53 //We have a bug when drawing minimized and reusing our bitmap
54 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
55 iCreateBitmap = false;
58 private void MainForm_Load(object sender, EventArgs e)
62 if (Properties.Settings.Default.DisplayConnectOnStartup)
70 private void buttonFont_Click(object sender, EventArgs e)
72 //fontDialog.ShowColor = true;
73 //fontDialog.ShowApply = true;
74 fontDialog.ShowEffects = true;
75 fontDialog.Font = marqueeLabelTop.Font;
77 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
79 //fontDialog.ShowHelp = true;
81 //fontDlg.MaxSize = 40;
82 //fontDlg.MinSize = 22;
84 //fontDialog.Parent = this;
85 //fontDialog.StartPosition = FormStartPosition.CenterParent;
87 //DlgBox.ShowDialog(fontDialog);
89 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
90 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
93 //MsgBox.Show("MessageBox MsgBox", "MsgBox caption");
95 //MessageBox.Show("Ok");
96 marqueeLabelTop.Font = fontDialog.Font;
97 marqueeLabelBottom.Font = fontDialog.Font;
98 Properties.Settings.Default.DisplayFont = fontDialog.Font;
99 Properties.Settings.Default.Save();
100 //label1.Font = fontDlg.Font;
101 //textBox1.BackColor = fontDlg.Color;
102 //label1.ForeColor = fontDlg.Color;
106 private void buttonCapture_Click(object sender, EventArgs e)
108 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
109 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
110 //Bitmap bmpToSave = new Bitmap(bmp);
111 bmp.Save("D:\\capture.png");
113 marqueeLabelTop.Text = "Sweet";
116 string outputFileName = "d:\\capture.png";
117 using (MemoryStream memory = new MemoryStream())
119 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
121 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
122 byte[] bytes = memory.ToArray();
123 fs.Write(bytes, 0, bytes.Length);
130 private void CheckForRequestResults()
132 if (iDisplay.IsRequestPending())
134 switch (iDisplay.AttemptRequestCompletion())
136 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
137 if (iDisplay.PowerSupplyStatus())
139 toolStripStatusLabelPower.Text = "ON";
143 toolStripStatusLabelPower.Text = "OFF";
145 //Issue next request then
146 iDisplay.RequestDeviceId();
149 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
150 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
151 //Issue next request then
152 iDisplay.RequestFirmwareRevision();
155 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
156 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
157 //No more request to issue
164 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
167 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
169 return aBmp.Width - aX - 1;
172 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
174 return iBmp.Height - aY - 1;
177 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
182 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
188 //This is our timer tick responsible to perform our render
189 private void timer_Tick(object sender, EventArgs e)
191 //Update our animations
192 DateTime NewTickTime = DateTime.Now;
194 marqueeLabelTop.UpdateAnimation(LastTickTime, NewTickTime);
195 marqueeLabelBottom.UpdateAnimation(LastTickTime, NewTickTime);
198 if (iDisplay.IsOpen())
200 CheckForRequestResults();
205 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
207 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
208 //iBmp.Save("D:\\capture.png");
210 //Select proper coordinate translation functions
211 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
212 CoordinateTranslationDelegate screenX;
213 CoordinateTranslationDelegate screenY;
215 if (Properties.Settings.Default.DisplayReverseScreen)
217 screenX = ScreenReversedX;
218 screenY = ScreenReversedY;
226 //Send it to our display
227 for (int i = 0; i < iBmp.Width; i++)
229 for (int j = 0; j < iBmp.Height; j++)
233 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
234 //For some reason when the app is minimized in the task bar only the alpha of our color is set.
235 //Thus that strange test for rendering to work both when the app is in the task bar and when it isn't.
236 iDisplay.SetPixel(screenX(iBmp, i), screenY(iBmp, j), Convert.ToInt32(!(color != 0xFF000000)));
241 iDisplay.SwapBuffers();
245 //Compute instant FPS
246 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " FPS";
248 LastTickTime = NewTickTime;
252 private void buttonOpen_Click(object sender, EventArgs e)
257 iDisplay.RequestPowerSupplyStatus();
262 toolStripStatusLabelConnect.Text = "Connection error";
267 private void buttonClose_Click(object sender, EventArgs e)
273 private void buttonClear_Click(object sender, EventArgs e)
276 iDisplay.SwapBuffers();
279 private void buttonFill_Click(object sender, EventArgs e)
282 iDisplay.SwapBuffers();
285 private void trackBarBrightness_Scroll(object sender, EventArgs e)
287 Properties.Settings.Default.DisplayBrightness = trackBarBrightness.Value;
288 Properties.Settings.Default.Save();
289 iDisplay.SetBrightness(trackBarBrightness.Value);
293 private void UpdateStatus()
295 if (iDisplay.IsOpen())
297 buttonFill.Enabled = true;
298 buttonClear.Enabled = true;
299 buttonOpen.Enabled = false;
300 buttonClose.Enabled = true;
301 trackBarBrightness.Enabled = true;
302 trackBarBrightness.Minimum = iDisplay.MinBrightness();
303 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
304 trackBarBrightness.Value = Properties.Settings.Default.DisplayBrightness;
305 trackBarBrightness.LargeChange = Math.Max(1,(iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5);
306 trackBarBrightness.SmallChange = 1;
307 iDisplay.SetBrightness(Properties.Settings.Default.DisplayBrightness);
309 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
310 //+ " - " + iDisplay.SerialNumber();
314 buttonFill.Enabled = false;
315 buttonClear.Enabled = false;
316 buttonOpen.Enabled = true;
317 buttonClose.Enabled = false;
318 trackBarBrightness.Enabled = false;
319 toolStripStatusLabelConnect.Text = "Disconnected";
325 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
327 //Save our show borders setting
328 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
329 Properties.Settings.Default.DisplayShowBorders = checkBoxShowBorders.Checked;
330 Properties.Settings.Default.Save();
333 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
335 //Save our connect on startup setting
336 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
337 Properties.Settings.Default.Save();
340 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
342 //Save our reverse screen setting
343 Properties.Settings.Default.DisplayReverseScreen = checkBoxReverseScreen.Checked;
344 Properties.Settings.Default.Save();
347 private void MainForm_Resize(object sender, EventArgs e)
349 if (WindowState == FormWindowState.Minimized)
352 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
353 iCreateBitmap = true;
357 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
363 public void StartServer()
365 iServiceHost = new ServiceHost
367 typeof(DisplayServer),
368 new Uri[] { new Uri("net.tcp://localhost:8001/") }
371 iServiceHost.AddServiceEndpoint(typeof(IDisplayService), new NetTcpBinding(SecurityMode.None,true), "DisplayService");
375 public void StopServer()
377 if (iClients.Count > 0 && !iClosing)
381 BroadcastCloseEvent();
385 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
387 iClosing = false; //We make sure we force close if asked twice
392 //We removed that as it often lags for some reason
393 //iServiceHost.Close();
397 public void BroadcastCloseEvent()
399 Trace.TraceInformation("BroadcastCloseEvent - start");
401 var inactiveClients = new List<string>();
402 foreach (var client in iClients)
404 //if (client.Key != eventData.ClientName)
408 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
409 client.Value.Callback.OnCloseOrder(/*eventData*/);
413 inactiveClients.Add(client.Key);
418 if (inactiveClients.Count > 0)
420 foreach (var client in inactiveClients)
422 iClients.Remove(client);
423 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
428 private void buttonStartClient_Click(object sender, EventArgs e)
430 Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
431 clientThread.Start();
434 private void buttonSuspend_Click(object sender, EventArgs e)
436 timer.Enabled = !timer.Enabled;
439 buttonSuspend.Text = "Suspend";
443 buttonSuspend.Text = "Pause";
447 private void buttonCloseClients_Click(object sender, EventArgs e)
449 BroadcastCloseEvent();
452 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
457 //Delegates are used for our thread safe method
458 public delegate void AddClientDelegate(string aSessionId, IDisplayServiceCallback aCallback);
459 public delegate void RemoveClientDelegate(string aSessionId);
460 public delegate void SetTextDelegate(string SessionId, int aLineIndex, string aText);
461 public delegate void SetTextsDelegate(string SessionId, System.Collections.Generic.IList<string> aTexts);
462 public delegate void SetClientNameDelegate(string aSessionId, string aName);
468 /// <param name="aSessionId"></param>
469 /// <param name="aCallback"></param>
470 public void AddClientThreadSafe(string aSessionId, IDisplayServiceCallback aCallback)
472 if (this.InvokeRequired)
474 //Not in the proper thread, invoke ourselves
475 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
476 this.Invoke(d, new object[] { aSessionId, aCallback });
480 //We are in the proper thread
481 //Add this session to our collection of clients
482 ClientData newClient = new ClientData(aSessionId, aCallback);
483 Program.iMainForm.iClients.Add(aSessionId, newClient);
484 //Add this session to our UI
485 UpdateClientTreeViewNode(newClient);
492 /// <param name="aSessionId"></param>
493 public void RemoveClientThreadSafe(string aSessionId)
495 if (this.InvokeRequired)
497 //Not in the proper thread, invoke ourselves
498 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
499 this.Invoke(d, new object[] { aSessionId });
503 //We are in the proper thread
504 //Remove this session from both client collection and UI tree view
505 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
507 Program.iMainForm.iClients.Remove(aSessionId);
508 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
511 if (iClosing && iClients.Count == 0)
513 //We were closing our form
514 //All clients are now closed
515 //Just resume our close operation
525 /// <param name="aLineIndex"></param>
526 /// <param name="aText"></param>
527 public void SetTextThreadSafe(string aSessionId, int aLineIndex, string aText)
529 if (this.InvokeRequired)
531 //Not in the proper thread, invoke ourselves
532 SetTextDelegate d = new SetTextDelegate(SetTextThreadSafe);
533 this.Invoke(d, new object[] { aSessionId, aLineIndex, aText });
537 ClientData client = iClients[aSessionId];
540 //Make sure all our texts are in place
541 while (client.Texts.Count < (aLineIndex + 1))
543 client.Texts.Add("");
545 client.Texts[aLineIndex] = aText;
547 //We are in the proper thread
548 //Only support two lines for now
551 marqueeLabelTop.Text = aText;
553 else if (aLineIndex == 1)
555 marqueeLabelBottom.Text = aText;
559 UpdateClientTreeViewNode(client);
567 /// <param name="aTexts"></param>
568 public void SetTextsThreadSafe(string aSessionId, System.Collections.Generic.IList<string> aTexts)
570 if (this.InvokeRequired)
572 //Not in the proper thread, invoke ourselves
573 SetTextsDelegate d = new SetTextsDelegate(SetTextsThreadSafe);
574 this.Invoke(d, new object[] { aSessionId, aTexts });
578 ClientData client = iClients[aSessionId];
581 //Populate our client with the given texts
583 foreach (string text in aTexts)
585 if (client.Texts.Count < (j + 1))
587 client.Texts.Add(text);
591 client.Texts[j]=text;
595 //We are in the proper thread
596 //Only support two lines for now
597 for (int i = 0; i < aTexts.Count; i++)
601 marqueeLabelTop.Text = aTexts[i];
605 marqueeLabelBottom.Text = aTexts[i];
610 UpdateClientTreeViewNode(client);
619 /// <param name="aSessionId"></param>
620 /// <param name="aName"></param>
621 public void SetClientNameThreadSafe(string aSessionId, string aName)
623 if (this.InvokeRequired)
625 //Not in the proper thread, invoke ourselves
626 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
627 this.Invoke(d, new object[] { aSessionId, aName });
631 //We are in the proper thread
633 ClientData client = iClients[aSessionId];
638 //Update our tree-view
639 UpdateClientTreeViewNode(client);
647 /// <param name="aClient"></param>
648 private void UpdateClientTreeViewNode(ClientData aClient)
655 TreeNode node = null;
656 //Check that our client node already exists
657 //Get our client root node using its key which is our session ID
658 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
661 //We already have a node for that client
663 //Clear children as we are going to recreate them below
668 //Client node does not exists create a new one
669 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
670 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
676 if (aClient.Name != "")
678 //We have a name, us it as text for our root node
679 node.Text = aClient.Name;
680 //Add a child with SessionId
681 node.Nodes.Add(new TreeNode(aClient.SessionId));
685 //No name, use session ID instead
686 node.Text = aClient.SessionId;
689 if (aClient.Texts.Count > 0)
691 //Create root node for our texts
692 TreeNode textsRoot = new TreeNode("Text");
693 node.Nodes.Add(textsRoot);
694 //For each text add a new entry
695 foreach (string text in aClient.Texts)
697 textsRoot.Nodes.Add(new TreeNode(text));
708 /// A UI thread copy of a client relevant data.
709 /// Keeping this copy in the UI thread helps us deal with threading issues.
711 public class ClientData
713 public ClientData(string aSessionId, IDisplayServiceCallback aCallback)
715 SessionId = aSessionId;
717 Texts = new List<string>();
718 Callback = aCallback;
721 public string SessionId { get; set; }
722 public string Name { get; set; }
723 public List<string> Texts { get; set; }
724 public IDisplayServiceCallback Callback { get; set; }