Source for gnu.awt.xlib.XEventLoop

   1: package gnu.awt.xlib;
   2: 
   3: /* Copyright (C) 2000  Free Software Foundation
   4: 
   5:    This file is part of libgcj.
   6: 
   7: This software is copyrighted work licensed under the terms of the
   8: Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
   9: details.  */
  10: 
  11: import java.awt.*;
  12: 
  13: import gnu.awt.LightweightRedirector;
  14: import gnu.gcj.xlib.Display;
  15: import gnu.gcj.xlib.XAnyEvent;
  16: import gnu.gcj.xlib.XExposeEvent;
  17: import gnu.gcj.xlib.XButtonEvent;
  18: import gnu.gcj.xlib.XConfigureEvent;
  19: import java.awt.event.PaintEvent;
  20: import java.awt.event.InputEvent;
  21: import java.awt.event.MouseEvent;
  22: import java.util.Vector;
  23: 
  24: public class XEventLoop implements Runnable
  25: {
  26:   Display display;
  27:   EventQueue queue;
  28:   XAnyEvent anyEvent;
  29:   private Thread eventLoopThread;
  30:   
  31:   LightweightRedirector lightweightRedirector = new LightweightRedirector();
  32:     
  33:   public XEventLoop(Display display, EventQueue queue)
  34:   {
  35:     this.display = display;
  36:     this.queue = queue;
  37:     
  38:     anyEvent = new XAnyEvent(display);
  39:     eventLoopThread = new Thread(this, "AWT thread for XEventLoop");
  40:     eventLoopThread.start();
  41:   }
  42: 
  43:   public void run ()
  44:   {
  45:     // FIXME: do we need an interrupt mechanism for window shutdown?
  46:     while (true)
  47:       postNextEvent (true);
  48:   }
  49:   
  50:   /** If there's an event available, post it.
  51:    * @return true if an event was posted
  52:    */
  53:   boolean postNextEvent(boolean block)
  54:   {
  55:     AWTEvent evt = getNextEvent(block);
  56:     if (evt != null)
  57:       queue.postEvent(evt);
  58:     return evt != null;
  59:   }
  60:     
  61:   /** Get the next event.
  62:    * @param block If true, block until an event becomes available
  63:    */
  64:   public AWTEvent getNextEvent(boolean block)
  65:   {
  66:     // ASSERT:
  67:     if (isIdle())
  68:       throw new Error("should not be idle");
  69:     
  70:     AWTEvent event = null;
  71:     if (loadNextEvent(block))
  72:       {
  73:         event = createEvent(); 
  74:         event = lightweightRedirector.redirect(event);
  75:       }
  76:     return event;
  77:   }
  78: 
  79:   boolean loadNextEvent(boolean block)
  80:   {
  81:     boolean gotEvent = false;
  82:     try
  83:       {
  84:     setIdle(true);
  85:     
  86:     /* The code below will result in an XFlush(). However,
  87:        while we are waiting for events after calling XFlush(),
  88:        new X requests issued on other threads will not
  89:        automatically be flushed. This can lead to a deadlock
  90:        since XFlush() will not be called before after the
  91:        processing of the next event, and new events arriving
  92:        might be dependent on the delivery of the X
  93:        requests. 
  94:        
  95:        Code that issues X requests should therefore call
  96:        flushIfIdle() after they're done, to ensure that the
  97:        requests are delivered in a timely manner.  XFlush is not
  98:        run if event processing is underway, since we are assured
  99:        that the event loop execution will return to this point,
 100:        where requests are flushed again, before waiting for new
 101:        events.
 102: 
 103:        Alternatively, do the work on the AWT thread, since the
 104:        XEventQueue knows how to flush the display when it runs out
 105:        of events. */
 106:     
 107:     //display.flush(); // implicit?
 108:     gotEvent = anyEvent.loadNext(block);
 109:       }
 110:     catch (RuntimeException re)
 111:       {
 112:     System.err.println("Exception thrown on event thread:" + re);
 113:       }
 114:     finally
 115:       {
 116:     setIdle(false);
 117:       }
 118:     return gotEvent;
 119:   }
 120:     
 121:   /**
 122:    * @returns an AWT event created based on the current XEvent.
 123:    * Returns null if the current XEvent does not map to any perticular
 124:    * AWT event.
 125:    */
 126:     
 127:   AWTEvent createEvent ()
 128:   {
 129:     int type = anyEvent.getType ();
 130:     // Ignore some events without further processing
 131:     switch (type)
 132:     {
 133:       // ignore "no expose" events, which are generated whenever a  pixmap
 134:       // is copied to copied to a window which is entirely unobscured
 135:       case XAnyEvent.TYPE_NO_EXPOSE:
 136:       case XAnyEvent.TYPE_UNMAP_NOTIFY:     // ignore for now
 137:       case XAnyEvent.TYPE_MAP_NOTIFY:       // ignore for now
 138:       case XAnyEvent.TYPE_REPARENT_NOTIFY:  // ignore for now
 139:         return null;
 140:       default:
 141:         break;  // continue processing events not in ignore list
 142:     }
 143:     /* avoid attempting to get client data before client data has
 144:        been set. */
 145:     Object peer;
 146:     synchronized (this)
 147:     {
 148:       peer = anyEvent.getWindow ().getClientData ();
 149:     }
 150:     
 151:     Component source = null;
 152:     
 153:     // Try to identify source component
 154:     
 155:     if (peer instanceof XCanvasPeer)
 156:     {
 157:       source = ((XCanvasPeer) peer).getComponent ();
 158:     }
 159:     
 160:     if (source == null)
 161:     {
 162:       String msg = "unable to locate source for event (" +
 163:       anyEvent + "): peer=" + peer;
 164:       throw new RuntimeException (msg);
 165:     }
 166:     
 167:     /* if a mapping from anyEvent to AWTEvent is possible, construct a
 168:        new AWTEvent and return it. */
 169:     
 170:     switch (type)
 171:     {
 172:       case XAnyEvent.TYPE_EXPOSE:
 173:         return createPaintEvent (source);
 174:       case XAnyEvent.TYPE_BUTTON_PRESS:
 175:       case XAnyEvent.TYPE_BUTTON_RELEASE:
 176:         return createMouseEvent (type, source);
 177:       case XAnyEvent.TYPE_CONFIGURE_NOTIFY:
 178:         configureNotify (peer);
 179:         return null;
 180:         
 181:       default:
 182:         String msg = "Do not know how to handle event (" + anyEvent + ")";
 183:         throw new RuntimeException (msg);
 184:     }
 185:   }
 186:   
 187:   AWTEvent createPaintEvent(Component src)
 188:   {
 189:     XExposeEvent expose = new XExposeEvent(anyEvent);
 190:     PaintEvent pe = new PaintEvent(src, PaintEvent.PAINT,
 191:                    expose.getBounds());
 192:     return pe;
 193:   }
 194:     
 195:   AWTEvent createMouseEvent(int type, Component src)
 196:   {    
 197:     XButtonEvent buttonEvt = new XButtonEvent(anyEvent);
 198:     
 199:     int modifiers = 0; //buttonToModifierMap[buttonEvt.button];
 200:     
 201:     /* Warning: this makes assumptions on the contents of
 202:        X.h... Button1 = 1, Button2 = 2, etc... */
 203:     switch (buttonEvt.button)
 204:       {
 205:       case 1:
 206:     modifiers = InputEvent.BUTTON1_DOWN_MASK;
 207:     break;
 208:       case 2:
 209:     modifiers = InputEvent.BUTTON2_DOWN_MASK;
 210:     break;
 211:       case 3:
 212:     modifiers = InputEvent.BUTTON2_DOWN_MASK;
 213:     break;
 214:       }
 215:     
 216:     int state = buttonEvt.state;
 217:     
 218:     // remap bits from state to modifiers:
 219:     
 220:     if ((state & XButtonEvent.MASK_SHIFT) != 0)
 221:       modifiers |= InputEvent.SHIFT_MASK;
 222:     
 223:     
 224:     if ((state & XButtonEvent.MASK_CONTROL) != 0)
 225:       modifiers |= InputEvent.CTRL_MASK;
 226:     
 227:     
 228:     /* FIXME: we need additional X code to properly map MODn states to
 229:        input modifiers */
 230:     
 231:     int clickCount = 1; // FIXME... Can't get this from X.
 232:     boolean popupTrigger = false; // FIXME: look up policy somewhere
 233:     
 234:     int x = buttonEvt.x;
 235:     int y = buttonEvt.y;
 236: 
 237:     int id = (type == XAnyEvent.TYPE_BUTTON_PRESS) ?
 238:       MouseEvent.MOUSE_PRESSED :
 239:       MouseEvent.MOUSE_RELEASED;
 240:     
 241:     MouseEvent me = new MouseEvent(src,
 242:                    id,
 243:                    buttonEvt.time, modifiers,
 244:                    buttonEvt.x, buttonEvt.y,
 245:                    clickCount, popupTrigger);
 246:     return me;
 247:   }
 248: 
 249:   void configureNotify(Object peerObj)
 250:   {
 251:     XConfigureEvent configEvent = new XConfigureEvent(anyEvent);
 252:     XFramePeer peer = (XFramePeer)  peerObj;
 253:     
 254:     peer.configureNotify(configEvent);
 255:   }
 256:     
 257:   public void flushIfIdle()
 258:   {
 259:     if (isIdle())
 260:       display.flush();
 261:   }
 262:   
 263:   volatile boolean idle = false;
 264: 
 265:   final synchronized void setIdle(boolean idle)
 266:   {
 267:     this.idle = idle;
 268:   }
 269: 
 270:   final synchronized boolean isIdle()
 271:   {
 272:     return idle;
 273:   }
 274: }