    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