001package org.vishia.gral.base;
002
003
004import java.util.EventObject;
005import java.util.concurrent.ConcurrentLinkedQueue;
006
007import org.vishia.event.EventTimerThread;
008import org.vishia.msgDispatch.LogMessage;
009import org.vishia.util.Assert;
010import org.vishia.util.MinMaxTime;
011
012/**This class is the base for implementation of graphic threading. It is implemented for SWT and Swing yet.
013 * <br><br>
014 * <b>The functionality and necessity of a graphic thread</b>: <br>
015 * Swing is not thread safe but it works if widgets are changed from other threads. 
016 * SWT causes an exception if a widget is changed in another thread than the graphic thread.
017 * The graphic thread in SWT calls the operation system routine to dispatch events. 
018 * If any event call-back method is invoked from the graphic system, it runs in the graphic thread.
019 * Therefore all other widgets can changed in this thread. For example a button is pressed, and therefore
020 * some widgets are changed in appearance (setText(), setColor()) or new widgets are created and other are deleted.
021 * <br><br>
022 * <b>Necessity of other threads than the graphic thread, divide of functionality in some threads</b>:<br> 
023 * To get the data from the application some actions should be done which may need time or/and may wait of something
024 * for example network communication transfer. If that action are done in the graphic thread immediately, 
025 * the graphic seems as frozen. It doesn't execute other activities which are need for example to resize a window,
026 * hide and focus it etc. This phenomena is known by some graphic applications. Often, firstly
027 * a pilot application works well because the actions are done in less 100 milliseconds. That's okay. But if the
028 * environment will be more and more complex and the functionality increases in real applications, the waiting
029 * time increases from 100 to ... 1 second and more, and the graphic hangs in some situations. It is bothering.    
030 * <b>Therefore long running or waiting actions should be organized in another thread.</b> The GUI can be show any hint
031 * that some action is executing. Other actions can be done in this time, especially abort a non-response action. 
032 * The GUI itself runs well.
033 * <br><br>
034 * To organize such working, actions on the GUI should notify and inform only the other threads. The other threads
035 * executes the necessary activity. If any activity is finished, its result should be shown on the graphic. 
036 * If any activity is progressed, it may be shown too, for example in steps of 300 milliseconds. 
037 * <br><br>
038 * The graphic content should be changed from some other thread. 
039 * But because the graphic isn't thread safe. The graphic can't be changed in the other thread directly.
040 * The information which should be presented should be queued. That is the mechanism:
041 * <ul>
042 * <li><b>Version 1:</b> An execution sequence for the graphic thread is written in a derived instance of
043 *    <ul><li>{@link GralGraphicTimeOrder#executeOrder(boolean)}.
044 *    </ul> 
045 *    That instance should be queued calling
046 *    <ul><li>{@link #addDispatchOrder(GralGraphicTimeOrder)}. 
047 *    </ul>
048 *    With them the graphic thread is woken up
049 *    because {@link #wakeup()} is called in the 'addDispatchOrder()'-routine. 
050 *    <br><br>
051 *    In the graphic thread execution loop the {@link #queueGraphicOrders} queue is checked 
052 *    and all queued method are invoked. That executes the 'widget.setText(text)' or the other routines
053 *    from the users programm in the {@link GralGraphicTimeOrder#executeOrder(boolean)}.
054 *    <br><br>
055 *    After the queue is checked the {@link #dispatchOsEvents()} is called. In SWT it calls the operation system
056 *    dispatching loop. If the underlying graphic system has its own graphic dispatching thread that thread
057 *    is woken up only to present the changes in the widgets. If all graphic dispatching is done, 
058 *    {@link #graphicThreadSleep()} let this thread sleeping, its all done. 
059 *    <br><br>
060 *    The instance of {@link GralGraphicTimeOrder} will be remain in the queue. For single activities
061 *    it should be queued out by itself calling its own {@link GralGraphicTimeOrder#removeFromList(GralGraphicThread)}
062 *    method in its {@link GralGraphicTimeOrder#executeOrder(boolean)}-routine.
063 *    Another possibility is to have instances of {@link GralGraphicTimeOrder} which are queued
064 *    for any time. They are invoked whenever {@link #wakeup()} is called. 
065 * <li><b>Version 2</b>: The order or commission can be instructed to the <code>setInfo(cmd, ...data)</code>-method
066 *   of a {@link GralWidget}:
067 *   <ul><li>{@link GralMng#setInfo(org.vishia.gral.base.GralWidget widget, int cmd, int ident, Object toshow, Object data)}
068 *   </ul>
069 *   This method fills a queue of the {@link GralMng}:
070 *   <ul><li>{@link GralMng.WidgetChangeRequExecuter#guiChangeRequests}
071 *     <li>{@link GralMng.WidgetChangeRequExecuter#executeOrder(boolean)} polls that queue.
072 *     <li>{@link GralMng#widgetChangeRequExecuter}: The instance in the GralWidgetManager.
073 *   </ul>
074 *   The instance is a permanent member of the {@link #queueGraphicOrders} queue, it is executed 
075 *   any time when a {@link #wakeup()} will be invoked. 
076 *   <br>
077 *   The commission for changing any widget
078 *   is given in that way with a specific command and data, which is realized in the method:
079 *   <ul><li>{@link GralMng#setInfoGthread(org.vishia.gral.ifc.GralWidget_ifc, int, int, Object, Object)}
080 *   </ul>
081 *   That method assigns some commands to the implementation level
082 *   standard invocation methods to change widget contents such as setText() or setColor(). To support this action
083 *   independently of the graphic implementation two interfaces are defined: 
084 *   <ul><li>{@link GralWidgetGthreadSet_ifc} and
085 *     <li> {@link GralWindow_setifc}
086 *   </ul>  
087 *   That interfaces defines the universal changed methods for widgets and windows.
088 *   <br><br>
089 *   The second version of instructing widget changing requires less programming effort, but it supports only standard operations.
090 * <li><b>Version 3</b>: All data of a widget are stored in graphic-independent fields of the derived instance of the widget.
091 *   That can be done in any thread without mutex mechanisms. If there are more as one thread 
092 *   which changes the same widget, the last wins. That may be a non-usual case. If one thread changes a text
093 *   and another thread changes a color, it may be okay. There is no necessity to make it thread-safe. 
094 *   But if it may be necessity, the user can wrap the access with specific synchronized methods.
095 *   After any changing of content, {@link GralWidget#repaint(int, int)} should be invoked with a suitable
096 *   delay. That queues the repaint instance
097 *   <ul><li>{@link GralWidget#repaintRequ} (it is private) in the queue
098 *   <li>{@link #queueDelayedGraphicOrders}
099 *   </ul>
100 *   In that way the repaint is executed in the graphic thread and fills the graphical widgets.
101 * </ul>  
102 * Secondary a system of delayed execution is given. All commissions to the graphic thread can be instruct with a delay.
103 * There are a queues for delayed execution and an extra time thread {@link #runTimer}. The starting time
104 * can be shelved if there are queued and re-instruct further time. This is necessary because some 
105 * graphic appearance changing requests may be given in any thread one after another, and the graphic thread
106 * should be invoked not for any one request, but only if all requests are given. It saves thread switch activity.
107 * Especially if some data are changed and a {@link GralWidget#repaint()} request only applies the data 
108 * to the widgets, that 'repaint()' should be invoked only if all data are given. But any data changing
109 * don't may know whether it is the last one. Therefore {@link GralWidget#repaint(int, int)} can be called
110 * after any data changing with shelving the repaint time. The repaint is executed not till the activity
111 * of data changing is finished.
112 *  
113 * @author Hartmut Schorrig
114 *
115 */
116public class GralGraphicThread implements Runnable
117{
118  
119  /**Version and history.
120   * <ul>
121   * <li>2016-07-16 Hartmut chg: The main window will be created with same methods like all other windows. 
122   * <li>2015-01-17 Hartmut chg: Now it is an own instance able to create before the graphic is established.
123   *   The graphical implementation extends the {@link ImplAccess}. 
124   * <li>2012-04-20 Hartmut bugfix: If a {@link GralGraphicTimeOrder} throws an exception,
125   *   it was started again because it was in the queue yet. The proplem occurs on build graphic. It
126   *   was repeated till all graphic handles are consumed. Now the {@link #queueGraphicOrders} entries
127   *   are deleted first, then executed. TODO use this class only for SWT, use the adequate given mechanism
128   *   for AWT: java.awt.EventQueue.invokeAndWait(Runnable). use Runnable instead GralDispatchCallbackWorker. 
129   * <li>2012-03-15 Hartmut chg: Message on exception.
130   * <li>2011-11-08 Hartmut new: Delayed orders to dispatch in the graphic thread: 
131   *   Some actions need some calculation time. 
132   *   If they are called in a fast repetition cycle, a follow up effect may occur. 
133   *   Therefore actions should be registered with a delayed start of execution, the start time 
134   *   should be able to putting off till all repetitions (for example key repetition) are done.
135   * <li>2011-11-00 Hartmut created: as own class from Swt widget manager.
136   * </ul>
137   * 
138   * <b>Copyright/Copyleft</b>:
139   * For this source the LGPL Lesser General Public License,
140   * published by the Free Software Foundation is valid.
141   * It means:
142   * <ol>
143   * <li> You can use this source without any restriction for any desired purpose.
144   * <li> You can redistribute copies of this source to everybody.
145   * <li> Every user of this source, also the user of redistribute copies
146   *    with or without payment, must accept this license for further using.
147   * <li> But the LPGL is not appropriate for a whole software product,
148   *    if this source is only a part of them. It means, the user
149   *    must publish this part of source,
150   *    but don't need to publish the whole source of the own product.
151   * <li> You can study and modify (improve) this source
152   *    for own using or for redistribution, but you have to license the
153   *    modified sources likewise under this LGPL Lesser General Public License.
154   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
155   * </ol>
156   * If you are indent to use this sources without publishing its usage, you can get
157   * a second license subscribing a special contract with the author. 
158   * 
159   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
160   * 
161   * 
162   */
163  public final static String version = "2015-01-17";
164  
165  //protected GralPrimaryWindow window;
166  
167  //protected Runnable init;
168  
169  /**The thread id of the managing thread for graphic actions. */
170  protected long graphicThreadId;
171
172  
173  boolean debugPrint = false;
174  
175  protected boolean isWakedUpOnly;
176  
177  /**True if the startup of the main window is done and the main window is visible. */
178  protected boolean bStarted = false; 
179
180  /** set to true to exit in main*/
181  protected boolean bExit = false;
182  
183  /**Instance to measure execution times.
184   * 
185   */
186  protected MinMaxTime checkTimes = new MinMaxTime();
187  
188  
189  /**Queue of orders to execute in the graphic thread before dispatching system events. 
190   * Any instance will be invoked in the dispatch-loop.
191   * See {@link #addTimeOrder(Runnable)}. 
192   * An order can be stayed in this queue for ever. It is invoked any time after the graphic thread 
193   * is woken up and before the dispatching of graphic-system-event will be started.
194   * An order may be run only one time, than it should delete itself from this queue in its run-method.
195   * */
196  private final ConcurrentLinkedQueue<GralGraphicTimeOrder> queueOrdersToExecute = new ConcurrentLinkedQueue<GralGraphicTimeOrder>();
197
198  
199  EventTimerThread orderList = new EventTimerThread("GraphicOrderTimeMng"); //this);
200
201  
202  private ImplAccess impl;
203  
204  
205  /**Constructs this class as superclass.
206   * The constructor of the inheriting class has some more parameter to build the 
207   * primary window. Therefore the {@link #threadGuiDispatch}.start() to start the {@link #run()}
208   * method of this class should be invoked only in the derived constructor
209   * after all parameter are saved to execute the overridden {@link #initGraphic()} method.
210   * @param name Name of the thread.
211   */
212  public GralGraphicThread() //char size)
213  { //sizeCharProperties = size;
214  }
215  
216  
217  /**Stores an event in the queue, able to invoke from any thread.
218   * @param ev
219   */
220  /*package private*/ void storeEvent(EventObject ev){
221    if(ev instanceof GralGraphicTimeOrder) { 
222      queueOrdersToExecute.add((GralGraphicTimeOrder)ev);
223      impl.wakeup();
224   } else {
225      throw new IllegalArgumentException("can only store events of type GralDispatchCallbackWorker");
226    }
227  }
228
229  
230  
231  public EventTimerThread orderList(){ return orderList; }
232  
233  
234  /**Adds the order to execute in the graphic dispatching thread.
235   * It is the same like order.{@link GralGraphicTimeOrder#activate()}.
236   * @param order
237   */
238  public void addDispatchOrder(GralGraphicTimeOrder order){ 
239    order.activate();
240    //orderList.addTimeOrder(order); 
241  }
242
243  //public void removeDispatchListener(GralDispatchCallbackWorker listener){ orderList.removeTimeOrder(listener); }
244
245  
246
247  
248  public void addEvent(EventObject event) {
249    assert(event instanceof GralGraphicTimeOrder);  //should be
250    queueOrdersToExecute.add((GralGraphicTimeOrder)event);
251    impl.wakeup();
252  }
253  
254  
255  public long getThreadIdGui(){ return graphicThreadId; }
256  
257  /**This method should wake up the execution of the graphic thread because some actions are registered.. */
258  public void wakeup(){ impl.wakeup(); }
259
260
261  public void waitForStart(){
262    synchronized(this) {
263      while(!bStarted) {
264        try{ wait(1000);
265        } catch(InterruptedException exc){}
266      }
267    }
268  }
269  
270  public boolean isStarted(){ return bStarted; }
271  
272  public boolean isRunning(){ return bStarted && !bExit; }
273  
274  public boolean isTerminated(){ return bStarted && bExit; }
275
276
277  
278  /**The run method of the graphic thread. This method is started in the constructor of the derived class
279   * of this, which implements the graphic system adapter. 
280   * <ul>
281   * <li>{@link #initGraphic()} will be called firstly. It is overridden by the graphic system implementing class
282   *   and does some things necessary for the graphic system implementing level.
283   * <li>The routine runs so long as {@link #bExit} is not set to false. bExit may be set to false 
284   *   in a window close listener of the graphic system level. It means, it is set to false especially 
285   *   if the windows will be closed from the operation system. If the window is closed because the application
286   *   is terminated by any application command the window will be closed, and the close listerer sets bReady
287   *   to false then. 
288   * <li>In the loop the {@link #queueGraphicOrders} will be executed.
289   * <li>For SWT graphic this is the dispatch loop of graphic events. They are executed 
290   *   in the abstract defined here {@link #dispatchOsEvents()} method.
291   * <li>This thread should be wait if not things are to do. The wait will be executed in the here abstract defined
292   *   method {@link #graphicThreadSleep()}.    
293   * </ul>  
294   * @see java.lang.Runnable#run()
295   */
296  @Override public void run()
297  { impl.initGraphic();
298    //add important properties for the main window, the user should not thing about.
299    impl.mainWindow.windProps |= GralWindow.windIsMain | GralWindow.windRemoveOnClose | GralWindow.windHasMenu;
300    //creates all widgets of this primary window.
301    impl.mainWindow.createImplWidget_Gthread();
302    impl.mainWindow.setWindowVisible( true ); 
303    GralPos pos = impl.mainWindow.pos();
304    if(pos.x.p2 == 0 && pos.y.p2 == 0){
305      impl.mainWindow.setFullScreen(true);  
306    }
307
308    //The last action, set the GuiThread
309    long guiThreadId1 = Thread.currentThread().getId(); ///
310    synchronized(this){
311      this.graphicThreadId = guiThreadId1;
312      orderList.start();
313      bStarted = true;
314      this.notify();      //wakeup the waiting calling thread.
315    }
316    checkTimes.init();
317    checkTimes.adjust();
318    checkTimes.cyclTime();
319    while (!bExit) {
320      step();
321    }
322    orderList.close();
323    //displaySwt.dispose ();
324    //bExit = true;
325    //synchronized(this){ notify(); }  //to weak up waiting on configGrafic().
326  }
327
328  
329  void step()
330  {
331    boolean bContinueDispatch;
332    int ctOsEvents = 0;
333    do{
334      try{ bContinueDispatch = impl.dispatchOsEvents();
335      /*  
336      try {
337          Thread.sleep(10);
338        } catch (InterruptedException e) {
339          // TODO Auto-generated catch block
340          e.printStackTrace();
341        }*/
342      }
343      catch(Throwable exc){
344        System.out.println(exc.getMessage());
345        exc.printStackTrace(System.out);
346        bContinueDispatch = true; //false;
347      }
348      ctOsEvents +=1;
349      //isWakedUpOnly = false;  //after 1 event, it may be wakeUp, set if false.
350    } while(bContinueDispatch);
351    if(debugPrint){ System.out.println("GralGraphicThread - dispatched os events, " + ctOsEvents); }
352    checkTimes.calcTime();
353    isWakedUpOnly = false;
354    //System.out.println("dispatched");
355    if(!bExit){
356      if(isWakedUpOnly){
357        Assert.stop();
358      }
359      //it may be waked up by the operation system or by calling Display.wake().
360      //if wakeUp() is called, isWakedUpOnly is set.
361      checkTimes.cyclTime();
362      //execute stored orders.
363      GralGraphicTimeOrder order;
364      boolean bSleep = true;
365      int ctOrders = 0;
366      while( (order = queueOrdersToExecute.poll()) !=null) {
367        order.stateOfEvent = 'r';
368        try{ 
369          order.doExecute();  //calls EventIimeOrderBase.doExecute() with enqueue
370        } catch(Throwable exc){
371          CharSequence excText = Assert.exceptionInfo("GralGraphicThread - unexpected Exception; ", exc, 0, 99);
372          System.err.append(excText);  //contains the stack trace in one line, up to 99 levels.
373        }
374        order.relinquish();
375        bSleep = false;
376        ctOrders +=1;
377      }
378      if(debugPrint){ System.out.println("GralGraphicThread - dispatched graphic orders, " + ctOrders); }
379      if(bSleep){ //if any order is executed, don't sleep yet because some os events may forced therefore. Dispatch it!
380        //no order executed. It sleeps. An os event which arrives in this time wakes up the graphic thread.
381        impl.graphicThreadSleep();
382      }
383    }    
384  }
385  
386  
387  
388  /**This class is used only for the implementation level of the graphic. It is not intent to use
389   * by any application. It is public because the implementation level should accesses it.
390   */
391  public static abstract class ImplAccess
392  {
393    protected final GralGraphicThread gralGraphicThread;
394    
395    protected char sizeCharProperties;
396
397    /**The thread which runs all graphical activities. */
398    protected final Thread threadGuiDispatch;
399
400    public final GralWindow mainWindow;
401    
402    protected final LogMessage log;
403    
404
405    //protected GrapGraphicThread
406
407    protected ImplAccess(char sizeCharProperties, GralWindow mainWindow, LogMessage log) {
408      this.mainWindow = mainWindow;
409      this.log = log;
410      this.gralGraphicThread = GralMng.get().gralDevice;
411      this.gralGraphicThread.impl = this;
412      this.sizeCharProperties = sizeCharProperties;
413      threadGuiDispatch = new Thread(gralGraphicThread, "graphic");
414
415    }
416    
417
418    protected void startThread() {
419      threadGuiDispatch.start();
420      gralGraphicThread.waitForStart();
421   }
422
423    
424    public GralGraphicThread gralGraphicThread(){ return gralGraphicThread; }
425    
426    /**This method should be implemented by the graphical implementation layer. It should build the graphic main window
427     * and returned when finished. This routine is called as the first routine in the Graphic thread's
428     * method {@link #run()}. See {@link org.vishia.gral.swt.SwtGraphicThread}. */
429    protected abstract void initGraphic();
430    
431    /**Calls the dispatch routine of the implementation graphic.
432     * @return true if dispatching should be continued.
433     */
434    protected abstract boolean dispatchOsEvents();
435    
436    
437    /**Forces the graphic thread to sleep and wait for any events. Either this routine returns
438     * if {@link #wakeup()} is called or this routine returns if the operation system wakes up the graphic thread. */
439    protected abstract void graphicThreadSleep();
440    
441    /**This method should be implemented by the graphical base. It should be waked up the execution 
442     * of the graphic thread because some actions are registered.. */
443    public abstract void wakeup();
444    
445    /**Terminates the run loop if val == true.
446     * @param val
447     */
448    protected void setClosed(boolean val){ gralGraphicThread.bExit = val; }
449    
450    //protected char sizeCharProperties(){ return gralGraphicThread.sizeCharProperties; }
451
452  }
453  
454}
455