OSDN Git Service

libjava/ChangeLog:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / peer / x / XEventPump.java
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.AWTEvent;
42 import java.awt.Component;
43 import java.awt.Container;
44 import java.awt.Graphics;
45 import java.awt.Insets;
46 import java.awt.Rectangle;
47 import java.awt.Toolkit;
48 import java.awt.Window;
49 import java.awt.event.ComponentEvent;
50 import java.awt.event.KeyEvent;
51 import java.awt.event.MouseEvent;
52 import java.awt.event.PaintEvent;
53 import java.awt.event.WindowEvent;
54 import java.util.HashMap;
55
56 import gnu.java.awt.ComponentReshapeEvent;
57 import gnu.x11.Atom;
58 import gnu.x11.Display;
59 import gnu.x11.event.ButtonPress;
60 import gnu.x11.event.ButtonRelease;
61 import gnu.x11.event.ClientMessage;
62 import gnu.x11.event.ConfigureNotify;
63 import gnu.x11.event.DestroyNotify;
64 import gnu.x11.event.Event;
65 import gnu.x11.event.Expose;
66 import gnu.x11.event.Input;
67 import gnu.x11.event.KeyPress;
68 import gnu.x11.event.KeyRelease;
69 import gnu.x11.event.MotionNotify;
70 import gnu.x11.event.PropertyNotify;
71 import gnu.x11.event.ResizeRequest;
72 import gnu.x11.event.UnmapNotify;
73
74 /**
75  * Fetches events from X, translates them to AWT events and pumps them up
76  * into the AWT event queue.
77  *
78  * @author Roman Kennke (kennke@aicas.com)
79  */
80 public class XEventPump
81   implements Runnable
82 {
83
84   /**
85    * The X Display from which we fetch and pump up events.
86    */
87   private Display display;
88
89   /**
90    * Maps X Windows to AWT Windows to be able to correctly determine the
91    * event targets.
92    */
93   private HashMap windows;
94
95   /**
96    * Indicates if we are currently inside a drag operation. This is
97    * set to the button ID when a button is pressed and to -1 (indicating
98    * that no drag is active) when the mouse is released.
99    */
100   private int drag;
101
102   /**
103    * Creates a new XEventPump for the specified X Display.
104    *
105    * @param d the X Display
106    */
107   XEventPump(Display d)
108   {
109     display = d;
110     windows = new HashMap();
111     drag = -1;
112     Thread thread = new Thread(this, "X Event Pump");
113     thread.setDaemon(true);
114     thread.start();
115   }
116
117   /**
118    * The main event pump loop. This basically fetches events from the
119    * X Display and pumps them into the system event queue.
120    */
121   public void run()
122   {
123     while (display.connected)
124       {
125         try
126           {
127             Event xEvent = display.next_event();
128             handleEvent(xEvent);
129           }
130         catch (ThreadDeath death)
131           {
132             // If someone wants to kill us, let them.
133             return;
134           }
135         catch (Throwable x)
136           {
137             System.err.println("Exception during event dispatch:");
138             x.printStackTrace(System.err);
139           }
140       }
141   }
142
143   /**
144    * Adds an X Window to AWT Window mapping. This is required so that the
145    * event pump can correctly determine the event targets.
146    *
147    * @param xWindow the X Window
148    * @param awtWindow the AWT Window
149    */
150   void registerWindow(gnu.x11.Window xWindow, Window awtWindow)
151   {
152     if (XToolkit.DEBUG)
153       System.err.println("registering window id: " + xWindow.id);
154     windows.put(new Integer(xWindow.id), awtWindow);
155   }
156
157   void unregisterWindow(gnu.x11.Window xWindow)
158   {
159     windows.remove(new Integer(xWindow.id));
160   }
161
162   private void handleButtonPress(ButtonPress event)
163   {
164     Integer key = new Integer(event.getEventWindowID());
165     Window awtWindow = (Window) windows.get(key);
166
167     // Create and post the mouse event.
168     int button = event.detail();
169
170     // AWT cannot handle more than 3 buttons and expects 0 instead.
171     if (button >= gnu.x11.Input.BUTTON3)
172       button = 0;
173     drag = button;
174
175     Component target =
176       findMouseEventTarget(awtWindow, event.getEventX(), event.getEventY());
177     if(target == null)
178       {
179         target = awtWindow;
180       }
181     
182     MouseEvent mp = new MouseEvent(target, MouseEvent.MOUSE_PRESSED,
183                                    System.currentTimeMillis(),
184                                    KeyboardMapping.mapModifiers(event.getState())
185                                      | buttonToModifier(button),
186                                    event.getEventX(), event.getEventY(),
187                                    1, false, button);
188     Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mp);
189   }
190   
191   private void handleButtonRelease(ButtonRelease event)
192   {
193     Integer key = new Integer(event.getEventWindowID());
194     Window awtWindow = (Window) windows.get(key);
195
196     int button = event.detail();
197     
198     // AWT cannot handle more than 3 buttons and expects 0 instead.
199     if (button >= gnu.x11.Input.BUTTON3)
200       button = 0;
201     drag = -1;
202     
203     Component target =
204       findMouseEventTarget(awtWindow, event.getEventX(), event.getEventY());
205     if(target == null)
206       {
207         target = awtWindow;
208       }
209     
210     MouseEvent mr = new MouseEvent(target, MouseEvent.MOUSE_RELEASED,
211                                    System.currentTimeMillis(),
212                                    KeyboardMapping.mapModifiers(event.getState())
213                                      | buttonToModifier(button),
214                                    event.getEventX(), event.getEventY(),
215                                    1, false, button);
216     Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mr);
217   }
218   
219   
220   private void handleMotionNotify(MotionNotify event)
221   {
222     Integer key = new Integer(event.getEventWindowID());
223     Window awtWindow = (Window) windows.get(key);
224
225     int button = event.detail();
226     
227     // AWT cannot handle more than 3 buttons and expects 0 instead.
228     if (button >= gnu.x11.Input.BUTTON3)
229       button = 0;
230
231     MouseEvent mm = null;
232     if (drag == -1)
233       {
234         mm = new MouseEvent(awtWindow, MouseEvent.MOUSE_MOVED,
235                             System.currentTimeMillis(),
236                             KeyboardMapping.mapModifiers(event.getState())
237                               | buttonToModifier(button),
238                             event.getEventX(), event.getEventY(),
239                             1, false);
240
241       }
242     else
243       {
244         mm = new MouseEvent(awtWindow, MouseEvent.MOUSE_DRAGGED,
245                             System.currentTimeMillis(),
246                             KeyboardMapping.mapModifiers(event.getState())
247                               | buttonToModifier(drag),
248                             event.getEventX(), event.getEventY(),
249                             1, false);
250       }
251     Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mm);
252   }
253    
254   // FIME: refactor and make faster, maybe caching the event and handle
255   // and/or check timing (timing is generated for PropertyChange)?
256   private void handleExpose(Expose event)
257   {
258     Integer key = new Integer(event.window_id);
259     Window awtWindow = (Window) windows.get(key);
260     
261     if (XToolkit.DEBUG)
262       System.err.println("expose request for window id: " + key);
263     
264     Rectangle r = new Rectangle(event.x(), event.y(), event.width(),
265                                 event.height());
266     // We need to clear the background of the exposed rectangle.
267     assert awtWindow != null : "awtWindow == null for window ID: " + key;
268      
269     Graphics g = awtWindow.getGraphics();
270     g.clearRect(r.x, r.y, r.width, r.height);
271     g.dispose();
272     
273     XWindowPeer xwindow = (XWindowPeer) awtWindow.getPeer();
274     Insets i = xwindow.insets();
275     if (event.width() != awtWindow.getWidth() - i.left - i.right
276         || event.height() != awtWindow.getHeight() - i.top - i.bottom)
277       {
278         int w = event.width();
279         int h = event.height();
280         int x = xwindow.xwindow.x;
281         int y = xwindow.xwindow.y;
282         
283         if (XToolkit.DEBUG)
284           System.err.println("Setting size on AWT window: " + w
285                            + ", " + h + ", " + awtWindow.getWidth()
286                            + ", " + awtWindow.getHeight());
287         
288         // new width and height
289         xwindow.xwindow.width = w;
290         xwindow.xwindow.height = h;
291         
292         // reshape the window
293         ComponentReshapeEvent cre =
294           new ComponentReshapeEvent(awtWindow, x, y, w, h);
295         awtWindow.dispatchEvent(cre);
296       }
297   
298     ComponentEvent ce =
299       new ComponentEvent(awtWindow, ComponentEvent.COMPONENT_RESIZED);
300     awtWindow.dispatchEvent(ce);
301     
302     PaintEvent pev = new PaintEvent(awtWindow, PaintEvent.UPDATE, r);
303     Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(pev);
304   }
305     
306   private void handleDestroyNotify(DestroyNotify destroyNotify)
307   {
308     if (XToolkit.DEBUG)
309       System.err.println("DestroyNotify event: " + destroyNotify);
310     
311     Integer key = new Integer(destroyNotify.event_window_id);
312     Window awtWindow = (Window) windows.get(key);
313     
314     AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_CLOSED);
315     Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
316   }
317   
318   private void handleClientMessage(ClientMessage clientMessage)
319   {
320     if (XToolkit.DEBUG)
321       System.err.println("ClientMessage event: " + clientMessage);
322     
323     if (clientMessage.delete_window())
324       {
325         if (XToolkit.DEBUG)
326           System.err.println("ClientMessage is a delete_window event");
327         
328         Integer key = new Integer(clientMessage.window_id);
329         Window awtWindow = (Window) windows.get(key);
330         
331         AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_CLOSING);
332         Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
333       }
334   }
335   
336   private void handleEvent(Event xEvent)
337   {
338     if (XToolkit.DEBUG)
339       System.err.println("fetched event: " + xEvent);
340     
341     switch (xEvent.code() & 0x7f)
342     {
343     case ButtonPress.CODE:
344       this.handleButtonPress((ButtonPress) xEvent);
345       break;
346     case ButtonRelease.CODE:
347       this.handleButtonRelease((ButtonRelease) xEvent); 
348       break;
349     case MotionNotify.CODE:
350       this.handleMotionNotify((MotionNotify) xEvent); 
351       break;
352     case Expose.CODE:
353       this.handleExpose((Expose) xEvent);
354       break;
355     case KeyPress.CODE:
356     case KeyRelease.CODE:
357       Integer key = new Integer(((Input) xEvent).getEventWindowID());
358       Window awtWindow = (Window) windows.get(key);
359       handleKeyEvent(xEvent, awtWindow);
360       break;
361     case DestroyNotify.CODE:
362       this.handleDestroyNotify((DestroyNotify) xEvent);
363       break;
364     case ClientMessage.CODE:
365       this.handleClientMessage((ClientMessage) xEvent);
366       break;
367     case PropertyNotify.CODE:
368       key = new Integer (((PropertyNotify) xEvent).getWindowID());
369       awtWindow = (Window) windows.get(key);
370       AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_STATE_CHANGED);
371       Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
372       break;
373     default:
374       if (XToolkit.DEBUG)
375         System.err.println("Unhandled X event: " + xEvent);
376     }
377   }
378
379   /**
380    * Handles key events from X.
381    *
382    * @param xEvent the X event
383    * @param awtWindow the AWT window to which the event gets posted
384    */
385   private void handleKeyEvent(Event xEvent, Window awtWindow)
386   {
387     Input keyEvent = (Input) xEvent;
388     int xKeyCode = keyEvent.detail();
389     int xMods = keyEvent.getState();
390     int keyCode = KeyboardMapping.mapToKeyCode(xEvent.display.input, xKeyCode,
391                                                xMods);
392     char keyChar = KeyboardMapping.mapToKeyChar(xEvent.display.input, xKeyCode,
393                                                 xMods);
394     if (XToolkit.DEBUG)
395       System.err.println("XEventPump.handleKeyEvent: " + xKeyCode + ", "
396                          + xMods + ": " + ((int) keyChar) + ", " + keyCode);
397     int awtMods = KeyboardMapping.mapModifiers(xMods);
398     long when = System.currentTimeMillis();
399     KeyEvent ke;
400     if (keyEvent.code() == KeyPress.CODE)
401       {
402         ke = new KeyEvent(awtWindow, KeyEvent.KEY_PRESSED, when,
403                           awtMods, keyCode,
404                           KeyEvent.CHAR_UNDEFINED);
405         Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
406         if (keyChar != KeyEvent.CHAR_UNDEFINED)
407           {
408             ke = new KeyEvent(awtWindow, KeyEvent.KEY_TYPED, when,
409                               awtMods, KeyEvent.VK_UNDEFINED,
410                               keyChar);
411             Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
412           }
413           
414       }
415     else
416       {
417         ke = new KeyEvent(awtWindow, KeyEvent.KEY_RELEASED, when,
418                           awtMods, keyCode,
419                           KeyEvent.CHAR_UNDEFINED);
420         Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
421       }
422
423   }
424
425   /** Translates an X button identifier to the AWT's MouseEvent modifier
426    *  mask. As the AWT cannot handle more than 3 buttons those return
427    *  <code>0</code>.
428    */
429   static int buttonToModifier(int button)
430   {
431     switch (button)
432     {
433       case gnu.x11.Input.BUTTON1:
434         return MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON1_MASK;
435       case gnu.x11.Input.BUTTON2:
436         return MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON2_MASK;
437       case gnu.x11.Input.BUTTON3:
438         return MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON3_MASK;
439     }
440
441     return 0;        
442   }
443   
444   /**
445    * Finds the heavyweight mouse event target.
446    *
447    * @param src the original source of the event
448    *
449    * @param pt the event coordinates
450    *
451    * @return the real mouse event target
452    */
453   private Component findMouseEventTarget(Component src, int x, int y)
454   {
455     Component found = null;
456     if (src instanceof Container)
457       {
458         Container cont = (Container) src;
459         int numChildren = cont.getComponentCount();
460         for (int i = 0; i < numChildren && found == null; i++)
461           {
462             Component child = cont.getComponent(i);
463             if (child != null && child.isVisible()
464                 && child.contains(x - child.getX(), y - child.getY()))
465               {
466                 if (child instanceof Container)
467                   {
468                     Component deeper = findMouseEventTarget(child,
469                                                             x - child.getX(),
470                                                             y - child.getY());
471                     if (deeper != null)
472                       found = deeper;
473                   }
474                 else if (! child.isLightweight())
475                   found = child;
476               }
477           }
478       }
479
480     // Consider the source itself.
481     if (found == null && src.contains(x, y) && ! src.isLightweight())
482       found = src;
483
484     return found;
485   }
486 }