1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
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
;
29 import java
.util
.NoSuchElementException
;
30 import javax
.microedition
.lcdui
.Display
;
31 import javax
.microedition
.lcdui
.DisplayListener
;
34 * Static state of the LCDUI sub-system.
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
=
48 /** The cached forms for {@link DisplayWidget}. */
49 private static final Map
<Reference
<DisplayWidget
>, UIDrawableBracket
>
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
;
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.
79 public static void addListener(DisplayListener __dl
)
80 throws NullPointerException
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
)
93 // Add it, if it is not there
99 * Returns the background thread used for LCDUI callbacks.
101 * @return The background thread, or {@code null} if not set.
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.
118 public static UIFormCallback
callback()
120 synchronized (StaticDisplayState
.class)
122 return StaticDisplayState
._CALLBACK
;
127 * Attempts to destroy the user interface.
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.
150 public static DisplayListener
[] listeners()
152 List
<DisplayListener
> listeners
= StaticDisplayState
._LISTENERS
;
153 synchronized (StaticDisplayState
.class)
155 return listeners
.<DisplayListener
>toArray(new DisplayListener
[
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.
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();
190 // {@squirreljme.error EB3e No widget exists for the given
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.
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.
222 public static UIDrawableBracket
locate(DisplayWidget __widget
, int __type
)
223 throws IllegalArgumentException
, NoSuchElementException
,
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.
244 public static UIDrawableBracket
locate(DisplayWidget __widget
, int __type
,
246 throws IllegalArgumentException
, NoSuchElementException
,
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
);
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();
275 Debugging
.debugNote("locate(...) -> possible = %s",
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
))
287 else if (__type
!= __backend
.widgetPropertyInt(
288 (UIWidgetBracket
)value
,
289 UIWidgetProperty
.INT_UIITEM_TYPE
, 0))
297 // {@squirreljme.error EB3c No form exists for the given
299 throw new NoSuchElementException("EB3c");
303 * Garbage collects the displays and forms.
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
);
324 Debugging
.debugNote("gc() -> %s",
327 // Remove from the mapping since it is gone now
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
);
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.
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.
374 public static void register(DisplayWidget __widget
,
375 UIDrawableBracket __native
)
376 throws NullPointerException
378 if (__widget
== null || __native
== null)
379 throw new NullPointerException("NARG");
382 Debugging
.debugNote("register(%s, %s)",
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
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
414 * @param __dl The listener to remove.
415 * @throws IllegalStateException If the listener is not in the display.
416 * @throws NullPointerException On null arguments.
419 public static void removeListener(DisplayListener __dl
)
420 throws IllegalStateException
, NullPointerException
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
)
436 // {@squirreljme.error EB1r The listener was never added to the
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.
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
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.
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();