ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/EmuXPortal/branches/mono/EmuXPortal/Form1.cs
Revision: 592
Committed: Fri Apr 22 01:21:20 2022 UTC (5 months ago) by william
File size: 13907 byte(s)
Log Message:
fix high-cpu usage due to input plugin refactor

File Contents

# Content
1 //#define DEV_DEBUG
2
3 #if DEV_DEBUG
4 #define USE_WINDOWED_MODE // when defined will use windowed mode instead of fullscreen
5 #endif
6 //#define DISABLE_PLUGINS // when defined plugin loading an their use will be disabled
7
8 //#define HAVE_X11_BORDERSTYLE_ERROR // indicates that there is an error with Control.set_InternalBorderStyle resulting in an X11 Error BadWindow (invalid Window parameter)
9
10 //#define DISABLE_WORKER_PROGRESS_EVENT // when defined will disable worker progress events
11 //#define DISAGLE_LOAD_SPLASH // when defined will not display a splash at loading (e.g. during cache building)
12
13 //#define DISABLE_CURSOR_HIDE // when this is present, the cursor will not be hidden
14 //#define DISABLE_PROGRESS_PERCENTAGE_MESSASGE // when this is present, no progress percent message will be shown on any progressbar
15
16
17 #if DEV_DEBUG
18 #warning DEV_DEBUG is enabled
19 #endif
20
21 using System;
22 using System.Collections.Generic;
23 using System.ComponentModel;
24 using System.Data;
25 using System.Drawing;
26 using System.Linq;
27 using System.Text;
28 using System.Windows.Forms;
29 using EmuXPortal.Api;
30 using System.Diagnostics;
31 using System.Reflection;
32 using System.Threading;
33 using Utilities.TransparentControls;
34 using Enterprise.Logging;
35 using System.IO;
36 using Enterprise.CrossPlatform;
37 using EmuXPortal.UI;
38 using EmuXPortal.UI.RenderControls;
39 using EmuXPortal.Plugins;
40 using irexec_api;
41
42 namespace EmuXPortal {
43 public partial class Form1 : Form {
44 static readonly System.Drawing.Color DEFAULT_TEXTFORECOLOR = System.Drawing.Color.Lime;
45 static readonly System.Drawing.Color DEFAULT_TEXTFORECOLOR_ERROR = System.Drawing.Color.Yellow;
46 static readonly System.Drawing.Color DEFAULT_TEXTFORECOLOR_LAUNCH = System.Drawing.Color.Blue;
47 static readonly System.Drawing.Color DEFAULT_TEXTFORECOLOR_MISSING = System.Drawing.Color.Red;
48
49 //const string irexec = "/usr/bin/irexec";
50 const string irexec = "irexec";
51 const string irexec_lircrc = "emuxportal.lircrc";
52 static int irexec_pid = 0;
53
54 Thread irexec_thread_monitor = null;
55
56 static bool bUserAbort = false;
57
58 private const int WINDOW_MODE_WIDTH = 900;
59 private const int WINDOW_MODE_HEIGHT = 500;
60 private const int INPUT_POLLING_FREQUENCY = 100;
61
62
63 private bool bRebuildCache = false;
64 private CustomProgressBar progress_bar;
65 private IPlatformCache cache = null;
66 private bool bConfigLoaded = false;
67
68 private System.Windows.Forms.Timer input_queue_handler;
69
70 private bool bInputQueuHandlerRunning = false;
71 private bool bInputQueueDisabled = false;
72
73 private Queue<KeyArrayArgs> event_queue;
74 private readonly object event_queue_lock = new object ();
75
76 private void do_irexec_monitor ()
77 {
78 var manager = IRExecManager.CreateManager ();
79 string arguments = Enterprise.CrossPlatform.OSInfo.FormatPath (string.Format ("{0}/{1}", Application.StartupPath, irexec_lircrc));
80 manager.SetIRExecArguments (arguments);
81
82 try {
83 while (true) {
84 manager.StartIRExec ();
85 Thread.Sleep (100);
86 }
87 } catch (ThreadAbortException) {
88 //gLog.Verbose.Error.WriteLine (ex.ToString ());
89 manager.StopIRExec ();
90 }
91
92 }
93
94 private void HookIRExec ()
95 {
96 //if (!OSInfo.OSIsUnix) { return; }
97 //KillDanglingIRExecProcesses ();
98 //gLog.Info.WriteLine ("Hooking Irexec...");
99 //using (Process p = new Process ()) {
100 // p.StartInfo.FileName = irexec;
101 // p.StartInfo.Arguments = Enterprise.CrossPlatform.OSInfo.FormatPath (string.Format ("{0}/{1}", Application.StartupPath, irexec_lircrc));
102 // p.Start ();
103 // irexec_pid = p.Id;
104 // gLog.Info.WriteLine ("Irexec Hooked [pid=0x{0:x8}]", irexec_pid);
105 //}
106 irexec_thread_monitor = new Thread (new ThreadStart (do_irexec_monitor));
107 irexec_thread_monitor.SetApartmentState (ApartmentState.STA);
108 irexec_thread_monitor.Start ();
109 }
110 private void UnHookIRExec ()
111 {
112 //if (!OSInfo.OSIsUnix) { return; }
113 //if (irexec_pid != 0) {
114 // gLog.Info.WriteLine ("UnHooking Irexec [pid=0x{0:x8}]", irexec_pid);
115 // try {
116 // using (Process p = Process.GetProcessById (irexec_pid)) {
117 // p.Kill ();
118 // gLog.Info.WriteLine ("UnHooked Irexec...");
119 // }
120 // } catch {
121 // gLog.Error.WriteLine ("Failed to UnHook Irexec...");
122 // }
123 //}
124 if (irexec_thread_monitor != null && irexec_thread_monitor.IsAlive) {
125 irexec_thread_monitor.Abort ();
126 irexec_thread_monitor = null;
127 }
128 }
129 static bool OnAbort ()
130 {
131
132 return bUserAbort;
133 }
134 #region unhandled exception support
135 static void Application_Unhandled_ThreadException (object sender, ThreadExceptionEventArgs e)
136 {
137 UnhandledExceptionEventArgs uea = new UnhandledExceptionEventArgs (e.Exception, false);
138 UnhandledExceptionEventHandler (sender, uea);
139 }
140 static void UnhandledExceptionEventHandler (object sender, UnhandledExceptionEventArgs args)
141 {
142 Exception ex = (args.ExceptionObject as Exception);
143 if (sender == null) {
144 gLog.Error.WriteLine ("Caught an unhandled exception from an unkown source");
145 } else {
146 gLog.Error.WriteLine ("Caught an unhandled exception from type: {0}", sender.GetType ().Name);
147 }
148
149 if (ex == null) {
150 gLog.Error.WriteLine ("The exception object was null -- it probably is not derived from System.Exception");
151 } else {
152 ex = ex.GetBaseException ();
153 gLog.Error.WriteLine ("{0}:", ex.GetType ().Name);
154 gLog.Verbose.Error.WriteLine (ex.ToString ());
155 }
156
157 }
158 #endregion
159 public Form1 ()
160 {
161 InitializeComponent ();
162 this.BackColor = Color.Black;
163 this.ForeColor = Color.LimeGreen;
164 this.progress_bar = new CustomProgressBar ();
165 this.KeyPreview = true;
166
167 #region logging support
168 string log_path = Application.StartupPath;
169 string log_filename = string.Format ("{0}.log", typeof (Form1).Assembly.GetName ().Name);
170 FileInfo log_file = new FileInfo (OSInfo.FormatPath (string.Format (@"{0}\{1}", log_path, log_filename)));
171
172 gLog.CreateLog (log_file.Name, true, LogLevel.kLogLevel_All_NoProgress, new EventHandler<LoggerOnFlushEventArgs> (Log_OnFlush));
173 LogLevel gLevel = gLog.LogLevel;
174 #if DEBUG
175 gLevel |= LogLevel.kLogLevel_Debug;
176 gLevel |= LogLevel.kLogLevel_VerboseDebug;
177 #else
178 gLevel &= ~LogLevel.kLogLevel_Debug;
179 gLevel &= ~LogLevel.kLogLevel_VerboseDebug;
180 #endif
181
182 gLog.SetLogLevel(gLevel);
183
184 #endregion
185
186 #region unhandled exception support
187 // AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionEventHandler);
188 // Application.ThreadException += Application_Unhandled_ThreadException;
189 #endregion
190
191 HookIRExec ();
192 #if !DISABLE_PLUGINS
193 event_queue = new Queue<KeyArrayArgs> ();
194 PluginLoader.LoadPlugins ();
195 PluginLoader.InitializeInputSystem ();
196
197 input_queue_handler = new System.Windows.Forms.Timer ();
198 input_queue_handler.Tick += Input_Queue_Handler_Tick;
199 input_queue_handler.Interval = INPUT_POLLING_FREQUENCY;
200 input_queue_handler.Enabled = true;
201 input_queue_handler.Start ();
202 #endif
203 }
204
205 #if !DISABLE_PLUGINS
206 void Input_Queue_Handler_Tick (object sender, EventArgs e)
207 {
208 if (bInputQueueDisabled) {
209 return;
210 }
211 //gLog.Verbose.Debug.WriteLine ("Input_Queue_Handler_Tick -- running");
212 if (bInputQueuHandlerRunning) { return; }
213 if (event_queue.Count > 0) {
214 bInputQueuHandlerRunning = true;
215 KeyArrayArgs key_event = null;
216 lock (event_queue_lock) {
217 key_event = event_queue.Dequeue ();
218 }
219 var keys = key_event.GetKeys ();
220 string key_string = KeyConverter.getSendKeysString (keys);
221 StringBuilder sb = new StringBuilder ();
222 keys.ToList ().ForEach ((Keys obj) => sb.AppendFormat (" {0}", obj));
223 //gLog.Verbose.Debug.WriteLine ("Sending keys: {0}", sb.ToString());
224 SendKeys.Send (key_string);
225 // process key_event
226 bInputQueuHandlerRunning = false;
227 }
228 }
229
230
231 void Handle_InputPoll (object sender, KeyArrayArgs e)
232 {
233 /*if (bInputQueueDisabled) {
234 // drop this input event
235 var keys = e.GetKeys ();
236 StringBuilder sb = new StringBuilder ();
237 if (keys.Length > 0) {
238 sb.AppendFormat ("{0}", keys [0]);
239 }
240 for (int i = 1; i < keys.Length; i++) {
241 sb.AppendFormat (", {0}", keys [i]);
242 }
243 gLog.Verbose.Warn.WriteLine ("Input Queue temporairly disabled....dropped key event ({0})!", sb.ToString());
244 return;
245 }
246 lock (event_queue_lock) {
247 event_queue.Enqueue (e);
248 }*/
249 if (!bInputQueueDisabled) {
250 lock (event_queue_lock) {
251 event_queue.Enqueue (e);
252 }
253 }
254 }
255 #endif
256
257
258 #region logging support
259 void Log_OnFlush (object sender, LoggerOnFlushEventArgs e) { OnLogFlush (e.Buffer); }
260
261
262
263
264 void OnLogFlush (string logmessage)
265 {
266 //if (this.IsDisposed) { return; }
267 ////UpdateStatus(logmessage);
268 ////UpdateLogOutput(logmessage);
269 //Application.DoEvents();
270 }
271 #endregion
272
273 private bool bCachedLoaded = false;
274 private bool OnCacheLoaded ()
275 {
276 return bCachedLoaded;
277 }
278
279
280 private void HandleCommandLine ()
281 {
282 var cargs = new List<String> (System.Environment.GetCommandLineArgs ());
283 if (cargs.Contains ("--rebuild")) {
284 bRebuildCache = true;
285 }
286 }
287
288
289 private void Form1_Load (object sender, EventArgs e)
290 {
291 HandleCommandLine ();
292 Config.InitializePresentationForm (this);
293 this.Show ();
294
295 }
296
297 private void Form1_Shown (object sender, EventArgs e)
298 {
299 this.Activated += Handle_Activated;
300
301 }
302
303
304 void Handle_Activated (object sender, EventArgs e)
305 {
306 this.Activated -= Handle_Activated;
307
308 #if DEV_DEBUG && USE_WINDOWED_MODE
309 this.Width = WINDOW_MODE_WIDTH;
310 this.Height = WINDOW_MODE_HEIGHT;
311 this.FormBorderStyle = FormBorderStyle.Sizable;
312 #endif
313
314 #if !DISAGLE_LOAD_SPLASH
315 SplashLoader loader = new SplashLoader (new AbortEvent (OnCacheLoaded));
316 loader.Show (this);
317 #endif
318 Stopwatch t = new Stopwatch ();
319 t.Start ();
320 try {
321 cache = PlatformCache.Create (new AbortEvent (OnAbort), bRebuildCache);
322 bCachedLoaded = true;
323 } catch (Exception ex) {
324 gLog.Error.WriteLine (ex.ToString ());
325 }
326 t.Stop ();
327 gLog.Profiler.WriteLine ("Cache generate/load took: {0}", TimeUtils.getTimeString (t));
328
329 t = new Stopwatch ();
330 t.Start ();
331 bConfigLoaded = Config.LoadConfig (cache);
332 t.Stop ();
333 gLog.Profiler.WriteLine ("Config load took: {0}", TimeUtils.getTimeString (t));
334
335 if (!bConfigLoaded) {
336 input_queue_handler.Stop ();
337 input_queue_handler.Enabled = false;
338 return;
339 }
340
341 /* initialize the container controls */
342 platform_flow.setCache (cache);
343 rom_flow.setCache (cache);
344
345
346
347 platform_flow.Font = this.Font;
348 rom_flow.Font = this.Font;
349
350 //platform_flow.setDefaultPlatformImage ((Bitmap)Properties.Resources.DefaultPlatformImage);
351 //rom_flow.setDefaultGameImage ((Bitmap)Properties.Resources.DefaultGameImage);
352
353 platform_flow.BackColor = this.BackColor;
354 platform_flow.ForeColor = this.ForeColor;
355
356 rom_flow.BackColor = this.BackColor;
357 rom_flow.ForeColor = this.ForeColor;
358
359
360 platform_flow.controlsRendered += ControlsRendered;
361 rom_flow.controlsRendered += ControlsRendered;
362
363 platform_flow.switchControls += SwitchControls;
364 rom_flow.switchControls += SwitchControls;
365
366 platform_flow.Parent = this;
367 rom_flow.Parent = this;
368
369
370 platform_flow.Width = this.Width - 10;
371 rom_flow.Width = this.Width - 10;
372
373 rom_flow.RomLaunched += Rom_Flow_RomLaunched;
374
375
376 if (!bConfigLoaded) {
377 gLog.Warn.WriteLine ("Terminating application...");
378 Application.Exit ();
379 return;
380 }
381 platform_flow.Visible = true;
382 #if !DISABLE_CURSOR_HIDE
383 Cursor.Hide ();
384 #else
385 Cursor.Show();
386 #endif
387
388 #if !DISABLE_PLUGINS
389 PluginLoader.StartInputPolling (bInputQueueDisabled, Handle_InputPoll);
390 #endif
391
392 }
393
394 void Rom_Flow_RomLaunched (object sender, RomLaunchArgs e)
395 {
396 var flags = e.RomLaunchFlags;
397 var info = e.RomInfo;
398 string game_name = "";
399 if (info == null) {
400 gLog.Warn.WriteLine ("recieved null rom info!");
401 } else {
402 game_name = info.RomTitle;
403 }
404
405 if (flags == RomLaunchFlags.BEGIN_LAUNCH) {
406 gLog.Debug.WriteLine ("Game '{0}' has been launched, disabling input queue",game_name);
407 bInputQueueDisabled = true;
408 PluginLoader.DisableInputPolling ();
409 } else if (flags == RomLaunchFlags.END_LAUNCH) {
410 gLog.Debug.WriteLine ("Game '{0}' has exited, re-enabling input queue",game_name);
411 bInputQueueDisabled = false;
412 PluginLoader.EnableInputPolling ();
413 } else {
414 if (flags != RomLaunchFlags.NONE) {
415 gLog.Debug.WriteLine ("Unknown rom launch flag: {0}", (int)flags);
416 }
417 }
418 }
419
420
421
422 void ControlsRendered (object sender, EventArgs e)
423 {
424
425 }
426
427 void SwitchControls (object sender, EventArgs e)
428 {
429 PlatformGridContrainer pc = (sender as PlatformGridContrainer);
430 if (pc != null) {
431 platform_flow.Visible = false;
432 rom_flow.Visible = true;
433 rom_flow.BringToFront ();
434 rom_flow.setCurrentRomPlatform (pc.getCurrentPlatform ());
435 rom_flow.Focus ();
436 rom_flow.renderControls ();
437 }
438 GameGridContainer gc = (sender as GameGridContainer);
439 if (gc != null) {
440 rom_flow.Visible = false;
441 platform_flow.Visible = true;
442 platform_flow.BringToFront ();
443 platform_flow.Focus ();
444 }
445 }
446
447
448
449 private void platform_flow_VisibleChanged (object sender, EventArgs e)
450 {
451 if (!platform_flow.Visible) return;
452
453 platform_flow.renderControls ();
454 }
455
456
457
458 private void rom_flow_VisibleChanged (object sender, EventArgs e)
459 {
460 if (!rom_flow.Visible) return;
461 }
462
463
464 private void Form1_FormClosing (object sender, FormClosingEventArgs e)
465 {
466 this.UnHookIRExec ();
467 }
468 private void Form1_FormClosed (object sender, FormClosedEventArgs e)
469 {
470 Cursor.Show ();
471 PluginLoader.EndInputPolling ();
472 }
473 }
474 }