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