Still trying to setup WCF for us to work nicely.
Now using multi threading and reliable session.
Implementing thread safe functions where needed.
Enforcing session mode.
Fixing bug in marquee label as we forgot to reset current position when text is changed.
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;
16 using SharpDisplayInterface;
17 using SharpDisplayClient;
20 namespace SharpDisplayManager
22 public partial class MainForm : Form
24 DateTime LastTickTime;
26 System.Drawing.Bitmap iBmp;
27 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
28 ServiceHost iServiceHost;
30 /// Our collection of clients
32 public Dictionary<string, IDisplayServiceCallback> iClients;
37 LastTickTime = DateTime.Now;
38 iDisplay = new Display();
39 iClients = new Dictionary<string, IDisplayServiceCallback>();
41 InitializeComponent();
45 marqueeLabelTop.Font = Properties.Settings.Default.DisplayFont;
46 marqueeLabelBottom.Font = Properties.Settings.Default.DisplayFont;
47 checkBoxShowBorders.Checked = Properties.Settings.Default.DisplayShowBorders;
48 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
49 checkBoxReverseScreen.Checked = Properties.Settings.Default.DisplayReverseScreen;
51 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
52 //We have a bug when drawing minimized and reusing our bitmap
53 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
54 iCreateBitmap = false;
57 private void MainForm_Load(object sender, EventArgs e)
61 if (Properties.Settings.Default.DisplayConnectOnStartup)
69 private void buttonFont_Click(object sender, EventArgs e)
71 //fontDialog.ShowColor = true;
72 //fontDialog.ShowApply = true;
73 fontDialog.ShowEffects = true;
74 fontDialog.Font = marqueeLabelTop.Font;
76 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
78 //fontDialog.ShowHelp = true;
80 //fontDlg.MaxSize = 40;
81 //fontDlg.MinSize = 22;
83 //fontDialog.Parent = this;
84 //fontDialog.StartPosition = FormStartPosition.CenterParent;
86 //DlgBox.ShowDialog(fontDialog);
88 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
89 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
92 //MsgBox.Show("MessageBox MsgBox", "MsgBox caption");
94 //MessageBox.Show("Ok");
95 marqueeLabelTop.Font = fontDialog.Font;
96 marqueeLabelBottom.Font = fontDialog.Font;
97 Properties.Settings.Default.DisplayFont = fontDialog.Font;
98 Properties.Settings.Default.Save();
99 //label1.Font = fontDlg.Font;
100 //textBox1.BackColor = fontDlg.Color;
101 //label1.ForeColor = fontDlg.Color;
105 private void buttonCapture_Click(object sender, EventArgs e)
107 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
108 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
109 //Bitmap bmpToSave = new Bitmap(bmp);
110 bmp.Save("D:\\capture.png");
112 marqueeLabelTop.Text = "Sweet";
115 string outputFileName = "d:\\capture.png";
116 using (MemoryStream memory = new MemoryStream())
118 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
120 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
121 byte[] bytes = memory.ToArray();
122 fs.Write(bytes, 0, bytes.Length);
129 private void CheckForRequestResults()
131 if (iDisplay.IsRequestPending())
133 switch (iDisplay.AttemptRequestCompletion())
135 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
136 if (iDisplay.PowerSupplyStatus())
138 toolStripStatusLabelPower.Text = "ON";
142 toolStripStatusLabelPower.Text = "OFF";
144 //Issue next request then
145 iDisplay.RequestDeviceId();
148 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
149 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
150 //Issue next request then
151 iDisplay.RequestFirmwareRevision();
154 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
155 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
156 //No more request to issue
163 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
166 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
168 return aBmp.Width - aX - 1;
171 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
173 return iBmp.Height - aY - 1;
176 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
181 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
187 //This is our timer tick responsible to perform our render
188 private void timer_Tick(object sender, EventArgs e)
190 //Update our animations
191 DateTime NewTickTime = DateTime.Now;
193 marqueeLabelTop.UpdateAnimation(LastTickTime, NewTickTime);
194 marqueeLabelBottom.UpdateAnimation(LastTickTime, NewTickTime);
197 if (iDisplay.IsOpen())
199 CheckForRequestResults();
204 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
206 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
207 //iBmp.Save("D:\\capture.png");
209 //Select proper coordinate translation functions
210 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
211 CoordinateTranslationDelegate screenX;
212 CoordinateTranslationDelegate screenY;
214 if (Properties.Settings.Default.DisplayReverseScreen)
216 screenX = ScreenReversedX;
217 screenY = ScreenReversedY;
225 //Send it to our display
226 for (int i = 0; i < iBmp.Width; i++)
228 for (int j = 0; j < iBmp.Height; j++)
232 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
233 //For some reason when the app is minimized in the task bar only the alpha of our color is set.
234 //Thus that strange test for rendering to work both when the app is in the task bar and when it isn't.
235 iDisplay.SetPixel(screenX(iBmp, i), screenY(iBmp, j), Convert.ToInt32(!(color != 0xFF000000)));
240 iDisplay.SwapBuffers();
244 //Compute instant FPS
245 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " FPS";
247 LastTickTime = NewTickTime;
251 private void buttonOpen_Click(object sender, EventArgs e)
256 iDisplay.RequestPowerSupplyStatus();
261 toolStripStatusLabelConnect.Text = "Connection error";
266 private void buttonClose_Click(object sender, EventArgs e)
272 private void buttonClear_Click(object sender, EventArgs e)
275 iDisplay.SwapBuffers();
278 private void buttonFill_Click(object sender, EventArgs e)
281 iDisplay.SwapBuffers();
284 private void trackBarBrightness_Scroll(object sender, EventArgs e)
286 Properties.Settings.Default.DisplayBrightness = trackBarBrightness.Value;
287 Properties.Settings.Default.Save();
288 iDisplay.SetBrightness(trackBarBrightness.Value);
292 private void UpdateStatus()
294 if (iDisplay.IsOpen())
296 buttonFill.Enabled = true;
297 buttonClear.Enabled = true;
298 buttonOpen.Enabled = false;
299 buttonClose.Enabled = true;
300 trackBarBrightness.Enabled = true;
301 trackBarBrightness.Minimum = iDisplay.MinBrightness();
302 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
303 trackBarBrightness.Value = Properties.Settings.Default.DisplayBrightness;
304 trackBarBrightness.LargeChange = Math.Max(1,(iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5);
305 trackBarBrightness.SmallChange = 1;
306 iDisplay.SetBrightness(Properties.Settings.Default.DisplayBrightness);
308 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
309 //+ " - " + iDisplay.SerialNumber();
313 buttonFill.Enabled = false;
314 buttonClear.Enabled = false;
315 buttonOpen.Enabled = true;
316 buttonClose.Enabled = false;
317 trackBarBrightness.Enabled = false;
318 toolStripStatusLabelConnect.Text = "Disconnected";
324 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
326 //Save our show borders setting
327 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
328 Properties.Settings.Default.DisplayShowBorders = checkBoxShowBorders.Checked;
329 Properties.Settings.Default.Save();
332 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
334 //Save our connect on startup setting
335 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
336 Properties.Settings.Default.Save();
339 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
341 //Save our reverse screen setting
342 Properties.Settings.Default.DisplayReverseScreen = checkBoxReverseScreen.Checked;
343 Properties.Settings.Default.Save();
346 private void MainForm_Resize(object sender, EventArgs e)
348 if (WindowState == FormWindowState.Minimized)
351 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
352 iCreateBitmap = true;
356 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
361 public void StartServer()
363 iServiceHost = new ServiceHost
365 typeof(DisplayServer),
366 new Uri[] { new Uri("net.tcp://localhost:8001/") }
369 iServiceHost.AddServiceEndpoint(typeof(IDisplayService), new NetTcpBinding(SecurityMode.None,true), "DisplayService");
373 public void StopServer()
375 //Tell connected client first? Is that possible?
377 if (iClients.Count>0)
380 BroadcastCloseEvent();
383 //iServiceHost.Close();
387 public void BroadcastCloseEvent()
389 var inactiveClients = new List<string>();
390 foreach (var client in iClients)
392 //if (client.Key != eventData.ClientName)
396 client.Value.OnServerClosing(/*eventData*/);
400 inactiveClients.Add(client.Key);
405 if (inactiveClients.Count > 0)
407 foreach (var client in inactiveClients)
409 iClients.Remove(client);
410 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
415 private void buttonStartClient_Click(object sender, EventArgs e)
417 Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
418 clientThread.Start();
421 private void buttonSuspend_Click(object sender, EventArgs e)
423 timer.Enabled = !timer.Enabled;
426 buttonSuspend.Text = "Suspend";
430 buttonSuspend.Text = "Pause";
434 private void buttonCloseClients_Click(object sender, EventArgs e)
436 BroadcastCloseEvent();
439 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
444 public delegate void AddClientDelegate(string aSessionId, IDisplayServiceCallback aCallback);
445 public delegate void RemoveClientDelegate(string aSessionId);
446 public delegate void SetTextDelegate(int aLineIndex, string aText);
447 public delegate void SetTextsDelegate(System.Collections.Generic.IList<string> aTexts);
453 /// <param name="aSessionId"></param>
454 /// <param name="aCallback"></param>
455 public void AddClientThreadSafe(string aSessionId, IDisplayServiceCallback aCallback)
457 if (this.treeViewClients.InvokeRequired)
459 //Not in the proper thread, invoke ourselves
460 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
461 this.Invoke(d, new object[] { aSessionId, aCallback });
465 //We are in the proper thread
466 //Add this session to our collection of clients
467 Program.iMainForm.iClients.Add(aSessionId, aCallback);
468 //Add this session to our UI
469 Program.iMainForm.treeViewClients.Nodes.Add(aSessionId, aSessionId);
476 /// <param name="aSessionId"></param>
477 public void RemoveClientThreadSafe(string aSessionId)
479 if (this.treeViewClients.InvokeRequired)
481 //Not in the proper thread, invoke ourselves
482 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
483 this.Invoke(d, new object[] { aSessionId });
487 //We are in the proper thread
488 //Remove this session from both client collection and UI tree view
489 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
491 Program.iMainForm.iClients.Remove(aSessionId);
492 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
500 /// <param name="aLineIndex"></param>
501 /// <param name="aText"></param>
502 public void SetTextThreadSafe(int aLineIndex, string aText)
504 if (this.treeViewClients.InvokeRequired)
506 //Not in the proper thread, invoke ourselves
507 SetTextDelegate d = new SetTextDelegate(SetTextThreadSafe);
508 this.Invoke(d, new object[] { aLineIndex, aText });
512 //We are in the proper thread
513 //Only support two lines for now
516 Program.iMainForm.marqueeLabelTop.Text = aText;
518 else if (aLineIndex == 1)
520 Program.iMainForm.marqueeLabelBottom.Text = aText;
528 /// <param name="aTexts"></param>
529 public void SetTextsThreadSafe(System.Collections.Generic.IList<string> aTexts)
531 if (this.treeViewClients.InvokeRequired)
533 //Not in the proper thread, invoke ourselves
534 SetTextsDelegate d = new SetTextsDelegate(SetTextsThreadSafe);
535 this.Invoke(d, new object[] { aTexts });
539 //We are in the proper thread
540 //Only support two lines for now
541 for (int i = 0; i < aTexts.Count; i++)
545 Program.iMainForm.marqueeLabelTop.Text = aTexts[i];
549 Program.iMainForm.marqueeLabelBottom.Text = aTexts[i];