Revert StaticDisplayState; For hosted only allow debug to be used.
[SquirrelJME.git] / modules / midp-lcdui / src / main / java / cc / squirreljme / runtime / lcdui / mle / StaticDisplayState.java
blobe806ed86c5bedf806cd7f12a40de823d8021f67d
1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the GNU General Public License v3+, or later.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package cc.squirreljme.runtime.lcdui.mle;
12 import cc.squirreljme.jvm.mle.brackets.UIDisplayBracket;
13 import cc.squirreljme.jvm.mle.brackets.UIDrawableBracket;
14 import cc.squirreljme.jvm.mle.brackets.UIFormBracket;
15 import cc.squirreljme.jvm.mle.brackets.UIItemBracket;
16 import cc.squirreljme.jvm.mle.brackets.UIWidgetBracket;
17 import cc.squirreljme.jvm.mle.callbacks.UIFormCallback;
18 import cc.squirreljme.jvm.mle.constants.UIItemType;
19 import cc.squirreljme.jvm.mle.constants.UIWidgetProperty;
20 import cc.squirreljme.runtime.cldc.debug.Debugging;
21 import cc.squirreljme.runtime.midlet.CleanupHandler;
22 import java.lang.ref.Reference;
23 import java.lang.ref.ReferenceQueue;
24 import java.lang.ref.WeakReference;
25 import java.util.LinkedHashMap;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.NoSuchElementException;
30 import javax.microedition.lcdui.Display;
31 import javax.microedition.lcdui.DisplayListener;
33 /**
34 * Static state of the LCDUI sub-system.
36 * @since 2020/07/01
38 public final class StaticDisplayState
40 /** The displays that are initialized currently. */
41 @SuppressWarnings("StaticVariableMayNotBeInitialized")
42 public static Display[] DISPLAYS;
44 /** Listeners for the display. */
45 private static final List<DisplayListener> _LISTENERS =
46 new LinkedList<>();
48 /** The cached forms for {@link DisplayWidget}. */
49 private static final Map<Reference<DisplayWidget>, UIDrawableBracket>
50 _WIDGETS =
51 new LinkedHashMap<>();
53 /** Queue which is used for garbage collection of forms. */
54 private static final ReferenceQueue<DisplayWidget> _QUEUE =
55 new ReferenceQueue<>();
57 /** Graphics handling thread. */
58 private static Thread _BACKGROUND_THREAD;
60 /** The callback used for form events. */
61 private static UIFormCallback _CALLBACK;
63 /** Did we add the cleanup handler yet? */
64 private static volatile boolean _addedCleanup;
66 /** Is this terminating? */
67 private static volatile boolean _IS_TERMINATING;
69 /**
70 * Adds the specified listener for changes to displays.
72 * The order in which listeners are executed in is
73 * implementation specified.
75 * @param __dl The listener to add.
76 * @throws NullPointerException On null arguments.
77 * @since 2020/07/01
79 public static void addListener(DisplayListener __dl)
80 throws NullPointerException
82 if (__dl == null)
83 throw new NullPointerException("NARG");
85 List<DisplayListener> listeners = StaticDisplayState._LISTENERS;
86 synchronized (StaticDisplayState.class)
88 // Do nothing if it is already in there
89 for (DisplayListener __listener : listeners)
90 if (__listener == __dl)
91 return;
93 // Add it, if it is not there
94 listeners.add(__dl);
98 /**
99 * Returns the background thread used for LCDUI callbacks.
101 * @return The background thread, or {@code null} if not set.
102 * @since 2020/09/12
104 public static Thread backgroundThread()
106 synchronized (StaticDisplayState.class)
108 return StaticDisplayState._BACKGROUND_THREAD;
113 * Returns the callback event handler.
115 * @return The callback for events.
116 * @since 2020/09/12
118 public static UIFormCallback callback()
120 synchronized (StaticDisplayState.class)
122 return StaticDisplayState._CALLBACK;
127 * Attempts to destroy the user interface.
129 * @since 2020/09/12
131 public static void destroy()
133 synchronized (StaticDisplayState.class)
135 // Remove all listeners
136 for (DisplayListener listener : StaticDisplayState.listeners())
137 StaticDisplayState.removeListener(listener);
139 // Perform garbage collection to clean up anything
140 StaticDisplayState.gc();
145 * Returns an array of all the attached listeners.
147 * @return An array of listeners.
148 * @since 2018/03/24
150 public static DisplayListener[] listeners()
152 List<DisplayListener> listeners = StaticDisplayState._LISTENERS;
153 synchronized (StaticDisplayState.class)
155 return listeners.<DisplayListener>toArray(new DisplayListener[
156 listeners.size()]);
161 * Locates the widget for the given native.
163 * @param __native The native to locate.
164 * @return The widget for the given native or {@code null} if not found.
165 * @throws NullPointerException On null arguments.
166 * @since 2020/09/20
168 public static DisplayWidget locate(UIDrawableBracket __native)
169 throws NoSuchElementException, NullPointerException
171 if (__native == null)
172 throw new NullPointerException("NARG");
174 // Would be previously cached
175 UIBackend instance = UIBackendFactory.getInstance(true);
176 synchronized (StaticDisplayState.class)
178 for (Map.Entry<Reference<DisplayWidget>, UIDrawableBracket> e :
179 StaticDisplayState._WIDGETS.entrySet())
181 if (instance.equals(__native, e.getValue()))
183 DisplayWidget rv = e.getKey().get();
184 if (rv != null)
185 return rv;
190 // {@squirreljme.error EB3e No widget exists for the given
191 // native.}
192 return null;
196 * Locates the widget for the given display widget.
198 * @param __widget The displayable to locate.
199 * @return The widget for the given displayable.
200 * @throws NoSuchElementException If none were found.
201 * @throws NullPointerException On null arguments.
202 * @since 2020/11/14
204 public static UIDrawableBracket locate(DisplayWidget __widget)
205 throws NoSuchElementException, NullPointerException
207 return StaticDisplayState.locate(__widget, Integer.MIN_VALUE);
211 * Locates the widget for the given display widget.
213 * @param __widget The displayable to locate.
214 * @param __type The {@link UIItemType} to look for, may be
215 * {@link Integer#MIN_VALUE} if not considered.
216 * @return The widget for the given displayable.
217 * @throws IllegalArgumentException If the type is not valid.
218 * @throws NoSuchElementException If none were found.
219 * @throws NullPointerException On null arguments.
220 * @since 2020/11/14
222 public static UIDrawableBracket locate(DisplayWidget __widget, int __type)
223 throws IllegalArgumentException, NoSuchElementException,
224 NullPointerException
226 return StaticDisplayState.locate(__widget, __type,
227 UIBackendFactory.getInstance(true));
231 * Locates the widget for the given display widget.
233 * @param __widget The displayable to locate.
234 * @param __type The {@link UIItemType} to look for, may be
235 * {@link Integer#MIN_VALUE} if not considered.
236 * @param __backend The backend to reference for properties, this is
237 * likely to only be used for testing purposes.
238 * @return The widget for the given displayable.
239 * @throws IllegalArgumentException If the type is not valid.
240 * @throws NoSuchElementException If none were found.
241 * @throws NullPointerException On null arguments.
242 * @since 2020/07/26
244 public static UIDrawableBracket locate(DisplayWidget __widget, int __type,
245 UIBackend __backend)
246 throws IllegalArgumentException, NoSuchElementException,
247 NullPointerException
249 if (__widget == null || __backend == null)
250 throw new NullPointerException("NARG");
252 // {@squirreljme.error EB39 Invalid item type. (The type)}
253 if ((__type < UIItemType.DISPLAY && __type != Integer.MIN_VALUE) ||
254 __type > UIItemType.NUM_TYPES)
255 throw new IllegalArgumentException("EB39 " + __type);
257 // Debug
258 if (false)
259 Debugging.debugNote("locate(%s, %d, %s)",
260 __widget, __type, __backend);
262 // Would be previously cached
263 synchronized (StaticDisplayState.class)
265 // Collect entries first
266 StaticDisplayState.gc();
268 // Run through entries
269 for (Map.Entry<Reference<DisplayWidget>, UIDrawableBracket> e :
270 StaticDisplayState._WIDGETS.entrySet())
272 DisplayWidget possible = e.getKey().get();
274 if (false)
275 Debugging.debugNote("locate(...) -> possible = %s",
276 possible);
278 if (possible == __widget)
280 UIDrawableBracket value = e.getValue();
282 // Are we looking for a specific type of item?
283 if (__type != Integer.MIN_VALUE)
284 if (__type == UIItemType.DISPLAY &&
285 !(value instanceof UIDisplayBracket))
286 continue;
287 else if (__type != __backend.widgetPropertyInt(
288 (UIWidgetBracket)value,
289 UIWidgetProperty.INT_UIITEM_TYPE, 0))
290 continue;
292 return value;
297 // {@squirreljme.error EB3c No form exists for the given
298 // displayable.}
299 throw new NoSuchElementException("EB3c");
303 * Garbage collects the displays and forms.
305 * @since 2020/07/01
307 public static void gc()
309 // Prevent thread mishaps between threads doing this
310 synchronized (StaticDisplayState.class)
312 ReferenceQueue<DisplayWidget> queue = StaticDisplayState._QUEUE;
313 Map<Reference<DisplayWidget>, UIDrawableBracket> widgets =
314 StaticDisplayState._WIDGETS;
316 // If there is anything in the queue, clear it out
317 for (Reference<? extends DisplayWidget> ref = queue.poll();
318 ref != null; ref = queue.poll())
320 UIDrawableBracket widget = widgets.get(ref);
322 // Notice
323 if (false)
324 Debugging.debugNote("gc() -> %s",
325 widget);
327 // Remove from the mapping since it is gone now
328 widgets.remove(ref);
330 // Perform collection on it
331 UIBackend instance = UIBackendFactory.getInstance(true);
332 if (widget instanceof UIFormBracket)
333 instance.formDelete((UIFormBracket)widget);
334 else if (widget instanceof UIItemBracket)
336 UIItemBracket item = (UIItemBracket)widget;
338 // The item could be part of a form still, so remove it
339 // from that form. If items happen to garbage collect
340 // before forms it will be removed
341 UIFormBracket form = instance.itemForm(item);
342 if (form != null)
343 instance.formItemRemove(form,
344 instance.formItemPosition(form, item));
346 instance.itemDelete(item);
353 * Checks if the UI is in the terminating state.
355 * @return If this is terminating.
356 * @since 2020/09/12
358 public static boolean isTerminating()
360 synchronized (StaticDisplayState.class)
362 return StaticDisplayState._IS_TERMINATING;
367 * Registers the displayable with the given form.
369 * @param __widget The displayable.
370 * @param __native The native to link.
371 * @throws NullPointerException On null arguments.
372 * @since 2020/07/01
374 public static void register(DisplayWidget __widget,
375 UIDrawableBracket __native)
376 throws NullPointerException
378 if (__widget == null || __native == null)
379 throw new NullPointerException("NARG");
381 if (false)
382 Debugging.debugNote("register(%s, %s)",
383 __widget, __native);
385 // Prevent thread mishaps between threads doing this
386 synchronized (StaticDisplayState.class)
388 // When terminating, destroy and cleanup all the display state
389 if (!StaticDisplayState._addedCleanup)
391 CleanupHandler.add(new __TerminateDisplay__());
393 // This should only be done once!
394 StaticDisplayState._addedCleanup = true;
397 // Perform quick garbage collection on forms in the event any
398 // have gone away
399 StaticDisplayState.gc();
401 // Queue the form for future cleanup
402 Reference<DisplayWidget> ref = new WeakReference<>(__widget,
403 StaticDisplayState._QUEUE);
405 // Bind this displayable to the form
406 StaticDisplayState._WIDGETS.put(ref, __native);
411 * Removes the specified display listener so that it is no longer called
412 * when events occur.
414 * @param __dl The listener to remove.
415 * @throws IllegalStateException If the listener is not in the display.
416 * @throws NullPointerException On null arguments.
417 * @since 2020/07/01
419 public static void removeListener(DisplayListener __dl)
420 throws IllegalStateException, NullPointerException
422 if (__dl == null)
423 throw new NullPointerException("NARG");
425 List<DisplayListener> listeners = StaticDisplayState._LISTENERS;
426 synchronized (StaticDisplayState.class)
428 boolean didRemove = false;
429 for (int i = 0, n = listeners.size(); i < n; i++)
430 if (listeners.get(i) == __dl)
432 listeners.remove(i);
433 didRemove = true;
436 // {@squirreljme.error EB1r The listener was never added to the
437 // listener set.}
438 if (!didRemove)
439 throw new IllegalStateException("EB1r");
444 * Sets the background thread.
446 * @param __thread The thread to set.
447 * @param __callback The callback for forms.
448 * @throws IllegalStateException If a thread was already set.
449 * @throws NullPointerException On null arguments.
450 * @since 2020/09/12
452 public static void setBackgroundThread(Thread __thread,
453 UIFormCallback __callback)
454 throws IllegalStateException, NullPointerException
456 if (__thread == null || __callback == null)
457 throw new NullPointerException("NARG");
459 synchronized (StaticDisplayState.class)
461 // {@squirreljme.error EB3d There is already a background thread
462 // present.}
463 if (StaticDisplayState._BACKGROUND_THREAD != null)
464 throw new IllegalStateException("EB3d");
466 StaticDisplayState._BACKGROUND_THREAD = __thread;
467 StaticDisplayState._CALLBACK = __callback;
472 * Terminates the user interface.
474 * @since 2020/09/12
476 public static void terminate()
478 synchronized (StaticDisplayState.class)
480 // Mark as terminating
481 StaticDisplayState._IS_TERMINATING = true;
483 // Interrupt the LCDUI thread so it knows to exit
484 Thread bgThread = StaticDisplayState._BACKGROUND_THREAD;
485 if (bgThread != null)
486 bgThread.interrupt();
488 // Notify everyone of the state change
489 StaticDisplayState.class.notifyAll();