Now requesting extra info on auto start-up too.
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)
67 if (Properties.Settings.Default.DisplayConnectOnStartup)
69 iDisplay.Open(Display.TMiniDisplayType.EMiniDisplayAutoDetect);
71 iDisplay.RequestPowerSupplyStatus();
76 private void buttonFont_Click(object sender, EventArgs e)
78 //fontDialog.ShowColor = true;
79 //fontDialog.ShowApply = true;
80 fontDialog.ShowEffects = true;
81 fontDialog.Font = marqueeLabelTop.Font;
83 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
85 //fontDialog.ShowHelp = true;
87 //fontDlg.MaxSize = 40;
88 //fontDlg.MinSize = 22;
90 //fontDialog.Parent = this;
91 //fontDialog.StartPosition = FormStartPosition.CenterParent;
93 //DlgBox.ShowDialog(fontDialog);
95 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
96 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
99 //MsgBox.Show("MessageBox MsgBox", "MsgBox caption");
101 //MessageBox.Show("Ok");
102 marqueeLabelTop.Font = fontDialog.Font;
103 marqueeLabelBottom.Font = fontDialog.Font;
104 Properties.Settings.Default.DisplayFont = fontDialog.Font;
105 Properties.Settings.Default.Save();
114 void CheckFontHeight()
116 if (marqueeLabelBottom.Font.Height > marqueeLabelBottom.Height)
118 labelWarning.Text = "WARNING: Selected font is too height by " + (marqueeLabelBottom.Font.Height - marqueeLabelBottom.Height) + " pixels!";
119 labelWarning.Visible = true;
123 labelWarning.Visible = false;
128 private void buttonCapture_Click(object sender, EventArgs e)
130 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
131 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
132 //Bitmap bmpToSave = new Bitmap(bmp);
133 bmp.Save("D:\\capture.png");
135 marqueeLabelTop.Text = "Sweet";
138 string outputFileName = "d:\\capture.png";
139 using (MemoryStream memory = new MemoryStream())
141 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
143 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
144 byte[] bytes = memory.ToArray();
145 fs.Write(bytes, 0, bytes.Length);
152 private void CheckForRequestResults()
154 if (iDisplay.IsRequestPending())
156 switch (iDisplay.AttemptRequestCompletion())
158 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
159 if (iDisplay.PowerSupplyStatus())
161 toolStripStatusLabelPower.Text = "ON";
165 toolStripStatusLabelPower.Text = "OFF";
167 //Issue next request then
168 iDisplay.RequestDeviceId();
171 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
172 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
173 //Issue next request then
174 iDisplay.RequestFirmwareRevision();
177 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
178 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
179 //No more request to issue
186 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
189 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
191 return aBmp.Width - aX - 1;
194 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
196 return iBmp.Height - aY - 1;
199 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
204 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
210 //This is our timer tick responsible to perform our render
211 private void timer_Tick(object sender, EventArgs e)
213 //Update our animations
214 DateTime NewTickTime = DateTime.Now;
216 marqueeLabelTop.UpdateAnimation(LastTickTime, NewTickTime);
217 marqueeLabelBottom.UpdateAnimation(LastTickTime, NewTickTime);
220 if (iDisplay.IsOpen())
222 CheckForRequestResults();
227 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
229 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
230 //iBmp.Save("D:\\capture.png");
232 //Select proper coordinate translation functions
233 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
234 CoordinateTranslationDelegate screenX;
235 CoordinateTranslationDelegate screenY;
237 if (Properties.Settings.Default.DisplayReverseScreen)
239 screenX = ScreenReversedX;
240 screenY = ScreenReversedY;
248 //Send it to our display
249 for (int i = 0; i < iBmp.Width; i++)
251 for (int j = 0; j < iBmp.Height; j++)
255 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
256 //For some reason when the app is minimized in the task bar only the alpha of our color is set.
257 //Thus that strange test for rendering to work both when the app is in the task bar and when it isn't.
258 iDisplay.SetPixel(screenX(iBmp, i), screenY(iBmp, j), Convert.ToInt32(!(color != 0xFF000000)));
263 iDisplay.SwapBuffers();
267 //Compute instant FPS
268 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " FPS";
270 LastTickTime = NewTickTime;
274 private void buttonOpen_Click(object sender, EventArgs e)
276 if (iDisplay.Open(Display.TMiniDisplayType.EMiniDisplayAutoDetect))
279 iDisplay.RequestPowerSupplyStatus();
284 toolStripStatusLabelConnect.Text = "Connection error";
289 private void buttonClose_Click(object sender, EventArgs e)
295 private void buttonClear_Click(object sender, EventArgs e)
298 iDisplay.SwapBuffers();
301 private void buttonFill_Click(object sender, EventArgs e)
304 iDisplay.SwapBuffers();
307 private void trackBarBrightness_Scroll(object sender, EventArgs e)
309 Properties.Settings.Default.DisplayBrightness = trackBarBrightness.Value;
310 Properties.Settings.Default.Save();
311 iDisplay.SetBrightness(trackBarBrightness.Value);
315 private void UpdateStatus()
317 if (iDisplay.IsOpen())
319 buttonFill.Enabled = true;
320 buttonClear.Enabled = true;
321 buttonOpen.Enabled = false;
322 buttonClose.Enabled = true;
323 trackBarBrightness.Enabled = true;
324 trackBarBrightness.Minimum = iDisplay.MinBrightness();
325 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
326 trackBarBrightness.Value = Properties.Settings.Default.DisplayBrightness;
327 trackBarBrightness.LargeChange = Math.Max(1,(iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5);
328 trackBarBrightness.SmallChange = 1;
329 iDisplay.SetBrightness(Properties.Settings.Default.DisplayBrightness);
331 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
332 //+ " - " + iDisplay.SerialNumber();
336 buttonFill.Enabled = false;
337 buttonClear.Enabled = false;
338 buttonOpen.Enabled = true;
339 buttonClose.Enabled = false;
340 trackBarBrightness.Enabled = false;
341 toolStripStatusLabelConnect.Text = "Disconnected";
347 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
349 //Save our show borders setting
350 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
351 Properties.Settings.Default.DisplayShowBorders = checkBoxShowBorders.Checked;
352 Properties.Settings.Default.Save();
355 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
357 //Save our connect on startup setting
358 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
359 Properties.Settings.Default.Save();
362 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
364 //Save our reverse screen setting
365 Properties.Settings.Default.DisplayReverseScreen = checkBoxReverseScreen.Checked;
366 Properties.Settings.Default.Save();
369 private void MainForm_Resize(object sender, EventArgs e)
371 if (WindowState == FormWindowState.Minimized)
374 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
375 iCreateBitmap = true;
379 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
385 public void StartServer()
387 iServiceHost = new ServiceHost
389 typeof(DisplayServer),
390 new Uri[] { new Uri("net.tcp://localhost:8001/") }
393 iServiceHost.AddServiceEndpoint(typeof(IDisplayService), new NetTcpBinding(SecurityMode.None,true), "DisplayService");
397 public void StopServer()
399 if (iClients.Count > 0 && !iClosing)
403 BroadcastCloseEvent();
407 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
409 iClosing = false; //We make sure we force close if asked twice
414 //We removed that as it often lags for some reason
415 //iServiceHost.Close();
419 public void BroadcastCloseEvent()
421 Trace.TraceInformation("BroadcastCloseEvent - start");
423 var inactiveClients = new List<string>();
424 foreach (var client in iClients)
426 //if (client.Key != eventData.ClientName)
430 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
431 client.Value.Callback.OnCloseOrder(/*eventData*/);
435 inactiveClients.Add(client.Key);
440 if (inactiveClients.Count > 0)
442 foreach (var client in inactiveClients)
444 iClients.Remove(client);
445 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
450 private void buttonStartClient_Click(object sender, EventArgs e)
452 Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
453 clientThread.Start();
457 private void buttonSuspend_Click(object sender, EventArgs e)
459 timer.Enabled = !timer.Enabled;
462 buttonSuspend.Text = "Suspend";
466 buttonSuspend.Text = "Pause";
470 private void buttonCloseClients_Click(object sender, EventArgs e)
472 BroadcastCloseEvent();
475 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
480 //Delegates are used for our thread safe method
481 public delegate void AddClientDelegate(string aSessionId, IDisplayServiceCallback aCallback);
482 public delegate void RemoveClientDelegate(string aSessionId);
483 public delegate void SetTextDelegate(string SessionId, TextField aTextField);
484 public delegate void SetTextsDelegate(string SessionId, System.Collections.Generic.IList<TextField> aTextFields);
485 public delegate void SetClientNameDelegate(string aSessionId, string aName);
491 /// <param name="aSessionId"></param>
492 /// <param name="aCallback"></param>
493 public void AddClientThreadSafe(string aSessionId, IDisplayServiceCallback aCallback)
495 if (this.InvokeRequired)
497 //Not in the proper thread, invoke ourselves
498 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
499 this.Invoke(d, new object[] { aSessionId, aCallback });
503 //We are in the proper thread
504 //Add this session to our collection of clients
505 ClientData newClient = new ClientData(aSessionId, aCallback);
506 Program.iMainForm.iClients.Add(aSessionId, newClient);
507 //Add this session to our UI
508 UpdateClientTreeViewNode(newClient);
515 /// <param name="aSessionId"></param>
516 public void RemoveClientThreadSafe(string aSessionId)
518 if (this.InvokeRequired)
520 //Not in the proper thread, invoke ourselves
521 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
522 this.Invoke(d, new object[] { aSessionId });
526 //We are in the proper thread
527 //Remove this session from both client collection and UI tree view
528 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
530 Program.iMainForm.iClients.Remove(aSessionId);
531 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
534 if (iClosing && iClients.Count == 0)
536 //We were closing our form
537 //All clients are now closed
538 //Just resume our close operation
548 /// <param name="aLineIndex"></param>
549 /// <param name="aText"></param>
550 public void SetTextThreadSafe(string aSessionId, TextField aTextField)
552 if (this.InvokeRequired)
554 //Not in the proper thread, invoke ourselves
555 SetTextDelegate d = new SetTextDelegate(SetTextThreadSafe);
556 this.Invoke(d, new object[] { aSessionId, aTextField });
560 ClientData client = iClients[aSessionId];
563 //Make sure all our texts are in place
564 while (client.Texts.Count < (aTextField.Index + 1))
566 //Add a text field with proper index
567 client.Texts.Add(new TextField(client.Texts.Count));
569 client.Texts[aTextField.Index] = aTextField;
571 //We are in the proper thread
572 //Only support two lines for now
573 if (aTextField.Index == 0)
575 marqueeLabelTop.Text = aTextField.Text;
576 marqueeLabelTop.TextAlign = aTextField.Alignment;
578 else if (aTextField.Index == 1)
580 marqueeLabelBottom.Text = aTextField.Text;
581 marqueeLabelBottom.TextAlign = aTextField.Alignment;
585 UpdateClientTreeViewNode(client);
593 /// <param name="aTexts"></param>
594 public void SetTextsThreadSafe(string aSessionId, System.Collections.Generic.IList<TextField> aTextFields)
596 if (this.InvokeRequired)
598 //Not in the proper thread, invoke ourselves
599 SetTextsDelegate d = new SetTextsDelegate(SetTextsThreadSafe);
600 this.Invoke(d, new object[] { aSessionId, aTextFields });
604 //We are in the proper thread
605 ClientData client = iClients[aSessionId];
608 //Populate our client with the given text fields
610 foreach (TextField textField in aTextFields)
612 if (client.Texts.Count < (j + 1))
614 client.Texts.Add(textField);
618 client.Texts[j] = textField;
622 //Only support two lines for now
623 for (int i = 0; i < aTextFields.Count; i++)
625 if (aTextFields[i].Index == 0)
627 marqueeLabelTop.Text = aTextFields[i].Text;
628 marqueeLabelTop.TextAlign = aTextFields[i].Alignment;
630 else if (aTextFields[i].Index == 1)
632 marqueeLabelBottom.Text = aTextFields[i].Text;
633 marqueeLabelBottom.TextAlign = aTextFields[i].Alignment;
638 UpdateClientTreeViewNode(client);
647 /// <param name="aSessionId"></param>
648 /// <param name="aName"></param>
649 public void SetClientNameThreadSafe(string aSessionId, string aName)
651 if (this.InvokeRequired)
653 //Not in the proper thread, invoke ourselves
654 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
655 this.Invoke(d, new object[] { aSessionId, aName });
659 //We are in the proper thread
661 ClientData client = iClients[aSessionId];
666 //Update our tree-view
667 UpdateClientTreeViewNode(client);
675 /// <param name="aClient"></param>
676 private void UpdateClientTreeViewNode(ClientData aClient)
683 TreeNode node = null;
684 //Check that our client node already exists
685 //Get our client root node using its key which is our session ID
686 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
689 //We already have a node for that client
691 //Clear children as we are going to recreate them below
696 //Client node does not exists create a new one
697 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
698 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
704 if (aClient.Name != "")
706 //We have a name, us it as text for our root node
707 node.Text = aClient.Name;
708 //Add a child with SessionId
709 node.Nodes.Add(new TreeNode(aClient.SessionId));
713 //No name, use session ID instead
714 node.Text = aClient.SessionId;
717 if (aClient.Texts.Count > 0)
719 //Create root node for our texts
720 TreeNode textsRoot = new TreeNode("Text");
721 node.Nodes.Add(textsRoot);
722 //For each text add a new entry
723 foreach (TextField field in aClient.Texts)
725 textsRoot.Nodes.Add(new TreeNode(field.Text));
733 private void buttonAddRow_Click(object sender, EventArgs e)
735 if (tableLayoutPanel.RowCount < 6)
737 tableLayoutPanel.RowCount++;
742 private void buttonRemoveRow_Click(object sender, EventArgs e)
744 if (tableLayoutPanel.RowCount > 1)
746 tableLayoutPanel.RowCount--;
751 private void buttonAddColumn_Click(object sender, EventArgs e)
753 if (tableLayoutPanel.ColumnCount < 8)
755 tableLayoutPanel.ColumnCount++;
760 private void buttonRemoveColumn_Click(object sender, EventArgs e)
762 if (tableLayoutPanel.ColumnCount > 1)
764 tableLayoutPanel.ColumnCount--;
769 private void buttonAlignLeft_Click(object sender, EventArgs e)
771 marqueeLabelTop.TextAlign = ContentAlignment.MiddleLeft;
772 marqueeLabelBottom.TextAlign = ContentAlignment.MiddleLeft;
775 private void buttonAlignCenter_Click(object sender, EventArgs e)
777 marqueeLabelTop.TextAlign = ContentAlignment.MiddleCenter;
778 marqueeLabelBottom.TextAlign = ContentAlignment.MiddleCenter;
781 private void buttonAlignRight_Click(object sender, EventArgs e)
783 marqueeLabelTop.TextAlign = ContentAlignment.MiddleRight;
784 marqueeLabelBottom.TextAlign = ContentAlignment.MiddleRight;
790 /// A UI thread copy of a client relevant data.
791 /// Keeping this copy in the UI thread helps us deal with threading issues.
793 public class ClientData
795 public ClientData(string aSessionId, IDisplayServiceCallback aCallback)
797 SessionId = aSessionId;
799 Texts = new List<TextField>();
800 Callback = aCallback;
803 public string SessionId { get; set; }
804 public string Name { get; set; }
805 public List<TextField> Texts { get; set; }
806 public IDisplayServiceCallback Callback { get; set; }