Source for gnu.java.awt.peer.x.XEventPump

   1: /* XEventPump.java -- Pumps events from X to AWT
   2:    Copyright (C) 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.awt.peer.x;
  40: 
  41: import java.awt.Graphics;
  42: import java.awt.Rectangle;
  43: import java.awt.Toolkit;
  44: import java.awt.Window;
  45: import java.awt.event.ComponentEvent;
  46: import java.awt.event.KeyEvent;
  47: import java.awt.event.MouseEvent;
  48: import java.awt.event.PaintEvent;
  49: import java.util.HashMap;
  50: 
  51: import gnu.x11.Display;
  52: import gnu.x11.event.ButtonPress;
  53: import gnu.x11.event.ButtonRelease;
  54: import gnu.x11.event.ConfigureNotify;
  55: import gnu.x11.event.Event;
  56: import gnu.x11.event.Expose;
  57: import gnu.x11.event.Input;
  58: import gnu.x11.event.KeyPress;
  59: import gnu.x11.event.KeyRelease;
  60: import gnu.x11.event.MotionNotify;
  61: 
  62: /**
  63:  * Fetches events from X, translates them to AWT events and pumps them up
  64:  * into the AWT event queue.
  65:  *
  66:  * @author Roman Kennke (kennke@aicas.com)
  67:  */
  68: public class XEventPump
  69:   implements Runnable
  70: {
  71: 
  72:   /**
  73:    * The X Display from which we fetch and pump up events.
  74:    */
  75:   private Display display;
  76: 
  77:   /**
  78:    * Maps X Windows to AWT Windows to be able to correctly determine the
  79:    * event targets.
  80:    */
  81:   private HashMap windows;
  82: 
  83:   /**
  84:    * Indicates if we are currently inside a drag operation. This is
  85:    * set to the button ID when a button is pressed and to -1 (indicating
  86:    * that no drag is active) when the mouse is released.
  87:    */
  88:   private int drag;
  89: 
  90:   /**
  91:    * Creates a new XEventPump for the specified X Display.
  92:    *
  93:    * @param d the X Display
  94:    */
  95:   XEventPump(Display d)
  96:   {
  97:     display = d;
  98:     windows = new HashMap();
  99:     drag = -1;
 100:     Thread thread = new Thread(this, "X Event Pump");
 101:     thread.setDaemon(true);
 102:     thread.start();
 103:   }
 104: 
 105:   /**
 106:    * The main event pump loop. This basically fetches events from the
 107:    * X Display and pumps them into the system event queue.
 108:    */
 109:   public void run()
 110:   {
 111:     while (display.connected)
 112:       {
 113:         try
 114:           {
 115:             Event xEvent = display.next_event();
 116:             handleEvent(xEvent);
 117:           }
 118:         catch (ThreadDeath death)
 119:           {
 120:             // If someone wants to kill us, let them.
 121:             return;
 122:           }
 123:         catch (Throwable x)
 124:           {
 125:             System.err.println("Exception during event dispatch:");
 126:             x.printStackTrace(System.err);
 127:           }
 128:       }
 129:   }
 130: 
 131:   /**
 132:    * Adds an X Window to AWT Window mapping. This is required so that the
 133:    * event pump can correctly determine the event targets.
 134:    *
 135:    * @param xWindow the X Window
 136:    * @param awtWindow the AWT Window
 137:    */
 138:   void registerWindow(gnu.x11.Window xWindow, Window awtWindow)
 139:   {
 140:     if (XToolkit.DEBUG)
 141:       System.err.println("registering window id: " + xWindow.id);
 142:     windows.put(new Integer(xWindow.id), awtWindow);
 143:   }
 144: 
 145:   void unregisterWindow(gnu.x11.Window xWindow)
 146:   {
 147:     windows.remove(new Integer(xWindow.id));
 148:   }
 149: 
 150:   private void handleEvent(Event xEvent)
 151:   {
 152: 
 153:     Integer key = null;
 154:     Window awtWindow = null;
 155: 
 156:     if (XToolkit.DEBUG)
 157:       System.err.println("fetched event: " + xEvent);
 158:     switch (xEvent.code())
 159:     {
 160:     case ButtonPress.CODE:
 161:       ButtonPress bp = (ButtonPress) xEvent;
 162:       key= new Integer(bp.event_window_id);
 163:       awtWindow = (Window) windows.get(key);
 164:       // Create and post the mouse event.
 165:       int button = bp.detail();
 166: 
 167:       // AWT cannot handle more than 3 buttons and expects 0 instead.
 168:       if (button >= gnu.x11.Input.BUTTON3)
 169:         button = 0;
 170:       drag = button;
 171: 
 172:       MouseEvent mp = new MouseEvent(awtWindow, MouseEvent.MOUSE_PRESSED,
 173:                                      System.currentTimeMillis(),
 174:                                      KeyboardMapping.mapModifiers(bp.state()) | buttonToModifier(button),
 175:                                      bp.event_x(), bp.event_y(),
 176:                                      1, false, button);
 177:       Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mp);
 178:       break;
 179:     case ButtonRelease.CODE:
 180:       ButtonRelease br = (ButtonRelease) xEvent;
 181:       key= new Integer(br.event_window_id);
 182:       awtWindow = (Window) windows.get(key);
 183: 
 184:       button = br.detail();
 185:       // AWT cannot handle more than 3 buttons and expects 0 instead.
 186:       if (button >= gnu.x11.Input.BUTTON3)
 187:         button = 0;
 188:       drag = -1;
 189:       MouseEvent mr = new MouseEvent(awtWindow, MouseEvent.MOUSE_RELEASED,
 190:                                      System.currentTimeMillis(),
 191:                                      KeyboardMapping.mapModifiers(br.state()) | buttonToModifier(button),
 192:                                      br.event_x(), br.event_y(),
 193:                                      1, false, button);
 194:       Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mr);
 195:       break;
 196:     case MotionNotify.CODE:
 197:       MotionNotify mn = (MotionNotify) xEvent;
 198:       key= new Integer(mn.event_window_id);
 199:       awtWindow = (Window) windows.get(key);
 200: 
 201:       MouseEvent mm;
 202:       if (drag == -1)
 203:         {
 204:           mm = new MouseEvent(awtWindow, MouseEvent.MOUSE_MOVED,
 205:                               System.currentTimeMillis(), 0,
 206:                               mn.event_x(), mn.event_y(),
 207:                               1, false);
 208:         }
 209:       else
 210:         {
 211:           mm = new MouseEvent(awtWindow, MouseEvent.MOUSE_DRAGGED,
 212:                               System.currentTimeMillis(), 0,
 213:                               mn.event_x(), mn.event_y(),
 214:                               1, false);
 215:         }
 216:       Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mm);
 217:       break;
 218:     case ConfigureNotify.CODE:
 219:       key= new Integer(((ConfigureNotify) xEvent).event_window_id);
 220:       awtWindow = (Window) windows.get(key);
 221:       ConfigureNotify c = (ConfigureNotify) xEvent;
 222:       if (XToolkit.DEBUG)
 223:         System.err.println("resize request for window id: " + key);
 224: 
 225:       // Detect and report size changes.
 226:       if (c.width() != awtWindow.getWidth()
 227:           || c.height() != awtWindow.getHeight())
 228:         {
 229:           if (XToolkit.DEBUG)
 230:             System.err.println("Setting size on AWT window: " + c.width()
 231:                              + ", " + c.height() + ", " + awtWindow.getWidth()
 232:                              + ", " + awtWindow.getHeight());
 233:           ((XWindowPeer) awtWindow.getPeer()).callback = true;
 234:           awtWindow.setSize(c.width(), c.height());
 235:           ((XWindowPeer) awtWindow.getPeer()).callback = false;
 236:         }
 237:       break;
 238:     case Expose.CODE:
 239:       key= new Integer(((Expose) xEvent).window_id);
 240:       awtWindow = (Window) windows.get(key);
 241:       Expose exp = (Expose) xEvent;
 242:       if (XToolkit.DEBUG)
 243:         System.err.println("expose request for window id: " + key);
 244:       Rectangle r = new Rectangle(exp.x(), exp.y(), exp.width(),
 245:                                   exp.height());
 246:       //System.err.println("expose paint: " + r);
 247:       // We need to clear the background of the exposed rectangle.
 248:       Graphics g = awtWindow.getGraphics();
 249:       g.clearRect(r.x, r.y, r.width, r.height);
 250:       g.dispose();
 251:       PaintEvent pev = new PaintEvent(awtWindow, PaintEvent.PAINT, r);
 252:       Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(pev);
 253:       break;
 254:     case KeyPress.CODE:
 255:     case KeyRelease.CODE:
 256:       key = new Integer(((Input) xEvent).event_window_id);
 257:       awtWindow = (Window) windows.get(key);
 258:       handleKeyEvent(xEvent, awtWindow);
 259:       break;
 260:     default:
 261:       if (XToolkit.DEBUG)
 262:         System.err.println("Unhandled X event: " + xEvent);
 263:     }
 264:   }
 265: 
 266:   /**
 267:    * Handles key events from X.
 268:    *
 269:    * @param xEvent the X event
 270:    * @param awtWindow the AWT window to which the event gets posted
 271:    */
 272:   private void handleKeyEvent(Event xEvent, Window awtWindow)
 273:   {
 274:     Input keyEvent = (Input) xEvent;
 275:     int xKeyCode = keyEvent.detail();
 276:     int xMods = keyEvent.state();
 277:     int keyCode = KeyboardMapping.mapToKeyCode(xEvent.display.input, xKeyCode,
 278:                                                xMods);
 279:     char keyChar = KeyboardMapping.mapToKeyChar(xEvent.display.input, xKeyCode,
 280:                                                 xMods);
 281:     if (XToolkit.DEBUG)
 282:       System.err.println("XEventPump.handleKeyEvent: " + xKeyCode + ", "
 283:                          + xMods + ": " + ((int) keyChar) + ", " + keyCode);
 284:     int awtMods = KeyboardMapping.mapModifiers(xMods);
 285:     long when = System.currentTimeMillis();
 286:     KeyEvent ke;
 287:     if (keyEvent.code() == KeyPress.CODE)
 288:       {
 289:         ke = new KeyEvent(awtWindow, KeyEvent.KEY_PRESSED, when,
 290:                           awtMods, keyCode,
 291:                           KeyEvent.CHAR_UNDEFINED);
 292:         Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
 293:         if (keyChar != KeyEvent.CHAR_UNDEFINED)
 294:           {
 295:             ke = new KeyEvent(awtWindow, KeyEvent.KEY_TYPED, when,
 296:                               awtMods, KeyEvent.VK_UNDEFINED,
 297:                               keyChar);
 298:             Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
 299:           }
 300:           
 301:       }
 302:     else
 303:       {
 304:         ke = new KeyEvent(awtWindow, KeyEvent.KEY_RELEASED, when,
 305:                           awtMods, keyCode,
 306:                           KeyEvent.CHAR_UNDEFINED);
 307:         Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
 308:       }
 309: 
 310:   }
 311: 
 312:   /** Translates an X button identifier to the AWT's MouseEvent modifier
 313:    *  mask. As the AWT cannot handle more than 3 buttons those return
 314:    *  <code>0</code>.
 315:    */
 316:   static int buttonToModifier(int button)
 317:   {
 318:     switch (button)
 319:     {
 320:       case gnu.x11.Input.BUTTON1:
 321:         return MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON1_MASK;
 322:       case gnu.x11.Input.BUTTON2:
 323:         return MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON2_MASK;
 324:       case gnu.x11.Input.BUTTON3:
 325:         return MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON3_MASK;
 326:     }
 327: 
 328:     return 0;        
 329:   }
 330: 
 331: }