1 /* Copyright (C) 2000, 2002, 2003  Free Software Foundation
2 
3    This file is part of libgcj.
4 
5 This software is copyrighted work licensed under the terms of the
6 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
7 details.  */
8 
9 package gnu.awt.xlib;
10 
11 import java.awt.Dimension;
12 import java.awt.BufferCapabilities;
13 import java.awt.Component;
14 import java.awt.EventQueue;
15 import java.awt.Rectangle;
16 import java.awt.Color;
17 import java.awt.Container;
18 import java.awt.Image;
19 import java.awt.GraphicsConfiguration;
20 import java.awt.Font;
21 import java.awt.FontMetrics;
22 import java.awt.Graphics;
23 import java.awt.Point;
24 import java.awt.Toolkit;
25 import java.awt.AWTEvent;
26 import java.awt.Cursor;
27 import java.awt.Shape;
28 
29 import java.awt.peer.*;
30 import java.awt.image.*;
31 
32 import java.awt.event.MouseListener;
33 import java.awt.event.PaintEvent;
34 
35 import java.util.EventListener;
36 
37 import gnu.gcj.xlib.WMSizeHints;
38 import gnu.gcj.xlib.Window;
39 import gnu.gcj.xlib.WindowAttributes;
40 import gnu.gcj.xlib.Display;
41 import gnu.gcj.xlib.Visual;
42 import gnu.gcj.xlib.Screen;
43 import gnu.gcj.xlib.XImage;
44 
45 import gnu.awt.j2d.*;
46 
47 import sun.awt.CausedFocusEvent;
48 
49 public class XCanvasPeer implements CanvasPeer
50 {
51   static final Dimension MIN_SIZE = new Dimension(1, 1);
52 
53   public // temporary
54 
55   Window window;
56   Window parent;
57 
58   Component component;
59   XGraphicsConfiguration config;
60   private WindowAttributes attributes = new WindowAttributes();
61   private long eventMask;
62 
XCanvasPeer(Component component)63   public XCanvasPeer(Component component)
64   {
65     this.component = component;
66 
67     // Set up graphics configuration (ie. screen + visual):
68 
69     config = (XGraphicsConfiguration)
70       component.getGraphicsConfiguration();
71 
72     if (config == null)
73       {
74 	// This will usually only happen for toplevel windows
75 	config = getXToolkit().getDefaultXGraphicsConfiguration();
76       }
77 
78     Rectangle bounds = component.getBounds();
79     parent = locateParentWindow(bounds);
80 
81     // Windows in X must atleast be of size 1x1
82     boolean boundsChanged = false;
83     if (bounds.width < 1)
84       {
85 	boundsChanged = true;
86 	bounds.width = 1;
87       }
88     if (bounds.height < 1)
89       {
90 	boundsChanged = true;
91 	bounds.height = 1;
92       }
93 
94     /* don't worry about this calling back to us, since the real
95        component object has not yet received a reference to this peer
96        object. */
97     component.setBounds(bounds);
98 
99 
100     /* Set background color */
101     Color bg = component.getBackground();
102     if (bg != null)
103       {
104 	int[] components =
105 	{
106 	  bg.getRed(),
107 	  bg.getGreen(),
108 	  bg.getBlue(),
109 	  0xff
110 	};
111 
112 	ColorModel cm = config.getColorModel();
113 	long pixel = cm.getDataElement(components, 0);
114 	attributes.setBackground(pixel);
115       }
116 
117     /* Set exposure mask so that we get exposure events
118        that can be translated into paint() calls. */
119     long eventMask = WindowAttributes.MASK_EXPOSURE;
120 
121     /* It would be nice to set up all other required events here, but
122        it is not possible to do so before after all the children of
123        this component has been realized.  The reason is that it is not
124        determined whether a component is lightweight before after the
125        addNotify() method has been called.  Thus, it is not possible
126        for parent component to determine what events it needs to
127        furnish for lightweight children.  Instead, we currently rely
128        on the component calling our setEventMask() method after the
129        correct event mask has been determined. */
130 
131     attributes.setEventMask(eventMask);
132 
133 
134     // TODO: set more window attributes?
135 
136     /* don't allow event queue to process events from the newly
137        created window before this peer has been registered as client
138        data. */
139     synchronized (getXToolkit().eventLoop)
140       {
141 	window = new gnu.gcj.xlib.Window(parent, bounds, attributes);
142 	window.setClientData(this); /* make it possible to find back
143 				       to this peer object. Used by
144 				       XEventQueue. */
145       }
146 
147     initWindowProperties();
148 
149     if (component.isVisible())
150       EventQueue.invokeLater(new DoMap(window));
151   }
152 
153   /**
154    * Override this in subclasses to implement other ways of obtaining
155    * parent windows.  Toplevel windows will typically have a different
156    * implementation.
157    */
locateParentWindow(Rectangle bounds)158   gnu.gcj.xlib.Window locateParentWindow(Rectangle bounds)
159   {
160     Container parent = component.getParent();
161     while (parent.isLightweight())
162       {
163 	bounds.x += parent.getX();
164 	bounds.y += parent.getY();
165 	parent = parent.getParent();
166 	// a null pointer here is a genuine error
167       }
168 
169     XCanvasPeer parentPeer = (XCanvasPeer) parent.getPeer();
170     if (parentPeer == null)
171       throw new NullPointerException("Parent has no peer. This should " +
172 				     "not be possible, since the " +
173 				     "calls leading here should come " +
174 				     "from parent, after it has " +
175 				     "set the parent peer.");
176     return parentPeer.window;
177   }
178 
179 
180   /**
181    * Template method to allow subclasses to apply properties to X11
182    * window right after creation.
183    */
initWindowProperties()184   void initWindowProperties()
185   {
186   }
187 
getXToolkit()188   XToolkit getXToolkit()
189   {
190     return XToolkit.INSTANCE;
191   }
192 
ensureFlush()193   protected void ensureFlush()
194   {
195     getXToolkit().flushIfIdle();
196   }
197 
getComponent()198   public Component getComponent()
199   {
200     return component;
201   }
202 
getBasicEventMask()203   long getBasicEventMask()
204   {
205     return WindowAttributes.MASK_EXPOSURE;
206   }
207 
208   // -------- java.awt.peer.ComponentPeer implementation
209 
checkImage(Image img, int width, int height, ImageObserver o)210   public int checkImage(Image img, int width, int height, ImageObserver o)
211   {
212     throw new UnsupportedOperationException("FIXME, not implemented");
213   }
createImage(ImageProducer prod)214   public Image createImage(ImageProducer prod)
215   {
216     return new XOffScreenImage (config, window, prod, config.getColorModel());
217   }
createImage(int width, int height)218   public Image createImage(int width, int height)
219   {
220     return new XOffScreenImage (config, window, width, height, config.getColorModel());
221   }
dispose()222   public void dispose()
223   {
224     throw new UnsupportedOperationException("FIXME, not implemented");
225   }
226 
getGraphicsConfiguration()227   public GraphicsConfiguration getGraphicsConfiguration()
228   {
229     return config;
230   }
231 
getFontMetrics(Font f)232   public FontMetrics getFontMetrics(Font f)
233   {
234     throw new UnsupportedOperationException("FIXME, not implemented");
235   }
236 
getColorModel()237   public ColorModel getColorModel ()
238   {
239     return null;
240   }
241 
getGraphics()242   public Graphics getGraphics()
243   {
244     DirectRasterGraphics gfxDevice = new XGraphics(window, config);
245     IntegerGraphicsState igState = new IntegerGraphicsState(gfxDevice);
246     Graphics2DImpl gfx2d = new Graphics2DImpl(config);
247 
248     gfx2d.setState(igState);
249     gfx2d.setColor(component.getBackground());
250     return gfx2d;
251   }
252 
253   private Rectangle locationBounds;
getLocationOnScreen()254   public Point getLocationOnScreen()
255   {
256     locationBounds = window.getBounds (locationBounds);
257     return new Point (locationBounds.x,locationBounds.y);
258   }
259 
getMinimumSize()260   public Dimension getMinimumSize ()
261   {
262     return MIN_SIZE;
263   }
264 
minimumSize()265   public Dimension minimumSize ()
266   {
267     return getMinimumSize ();
268   }
269 
getPreferredSize()270   public Dimension getPreferredSize ()
271   {
272     return component.getSize();
273   }
274 
preferredSize()275   public Dimension preferredSize ()
276   {
277     return getPreferredSize();
278   }
279 
getToolkit()280   public Toolkit getToolkit()
281   {
282     return getXToolkit();
283   }
284 
handleEvent(AWTEvent event)285   public void handleEvent(AWTEvent event)
286   {
287     int id = event.getID ();
288 
289     switch (id)
290     {
291       case PaintEvent.PAINT:
292       case PaintEvent.UPDATE:
293       {
294         try
295         {
296           Graphics g = getGraphics ();
297           g.setClip (((PaintEvent)event).getUpdateRect ());
298 
299           if (id == PaintEvent.PAINT)
300             component.paint (g);
301           else
302             component.update (g);
303 
304           g.dispose ();
305         }
306         catch (InternalError e)
307         {
308           System.err.println (e);
309         }
310       }
311       break;
312     }
313   }
314 
isFocusTraversable()315   public boolean isFocusTraversable()
316   {
317     throw new UnsupportedOperationException("FIXME, not implemented");
318   }
319 
paint(Graphics gfx)320   public void paint(Graphics gfx)
321   {
322     // do nothing by default
323   }
324 
prepareImage(Image img, int width, int height, ImageObserver o)325   public boolean prepareImage(Image img, int width, int height,
326 			      ImageObserver o)
327   {
328     throw new UnsupportedOperationException("FIXME, not implemented");
329   }
330 
print(Graphics graphics)331   public void print(Graphics graphics)
332   {
333     paint(graphics);
334   }
335 
repaint(long tm, int x, int y, int w, int h)336   public void repaint(long tm, int x, int y, int w, int h)
337   {
338     /* TODO?
339 
340        X allows intelligent X servers to do smart
341        refreshing. Perhaps involve X in repainting of components,
342        rather that keeping it all within the local event queue. */
343 
344     PaintEvent updateEvent = new PaintEvent(component,
345 					    PaintEvent.UPDATE,
346 					    new Rectangle(x, y, w, h));
347     getXToolkit().queue.postEvent(updateEvent);
348   }
349 
requestFocus()350   public void requestFocus()
351   {
352     throw new UnsupportedOperationException("FIXME, not implemented");
353   }
354 
setBackground(Color color)355   public void setBackground(Color color)
356   {
357     if (color != null)
358     {
359       int[] components =
360       {
361         color.getRed (),
362         color.getGreen (),
363         color.getBlue (),
364         0xff
365       };
366 
367       ColorModel cm = config.getColorModel ();
368       long pixel = cm.getDataElement (components, 0);
369       attributes.setBackground (pixel);
370       window.setAttributes (attributes);
371     }
372   }
373 
setBounds(int x, int y, int width, int height)374   public void setBounds(int x, int y, int width, int height)
375   {
376     width  = Math.max(width,  1);
377     height = Math.max(height, 1);
378     window.setBounds(x, y, width, height);
379     ensureFlush();
380   }
381 
reshape(int x, int y, int width, int height)382   public void reshape (int x, int y, int width, int height)
383   {
384     setBounds (x, y, width, height);
385   }
386 
setCursor(Cursor cursor)387   public void setCursor(Cursor cursor)
388   {
389     throw new UnsupportedOperationException("FIXME, not implemented");
390   }
391 
setEnabled(boolean enabled)392   public void setEnabled(boolean enabled)
393   {
394     throw new UnsupportedOperationException("FIXME, not implemented");
395   }
396 
enable()397   public void enable ()
398   {
399     setEnabled (true);
400   }
401 
disable()402   public void disable ()
403   {
404     setEnabled (false);
405   }
406 
setEventMask(long eventMask)407   public void setEventMask(long eventMask)
408   {
409     if (this.eventMask != eventMask)
410     {
411       this.eventMask = eventMask;
412       long xEventMask = getBasicEventMask ();
413 
414       if ((eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0)
415       {
416         xEventMask |=
417           WindowAttributes.MASK_BUTTON_PRESS |
418           WindowAttributes.MASK_BUTTON_RELEASE;
419       }
420 
421       attributes.setEventMask (xEventMask);
422       window.setAttributes (attributes);
423       ensureFlush ();
424     }
425   }
426 
setFont(Font font)427   public void setFont(Font font)
428   {
429     /* default canvas peer does not keep track of font, since it won't
430        paint anything. */
431   }
432 
setForeground(Color color)433   public void setForeground(Color color)
434   {
435     /* default canvas peer does not keep track of foreground, since it won't
436        paint anything. */
437   }
438 
setVisible(boolean visible)439   public void setVisible(boolean visible)
440   {
441     if (visible)
442       {
443 	window.map();
444 	ensureFlush();
445       }
446     else
447       {
448 	window.unmap();
449 	ensureFlush();
450       }
451   }
452 
show()453   public void show ()
454   {
455     setVisible (true);
456   }
457 
hide()458   public void hide ()
459   {
460     setVisible (false);
461   }
462 
isFocusable()463   public boolean isFocusable ()
464   {
465     return false;
466   }
467 
requestFocus(Component source, boolean b1, boolean b2, long x)468   public boolean requestFocus (Component source, boolean b1,
469                                boolean b2, long x)
470   {
471     return false;
472   }
473 
requestFocus(Component source, boolean b1, boolean b2, long x, CausedFocusEvent.Cause cause)474   public boolean requestFocus (Component source, boolean b1,
475                                boolean b2, long x,
476 			       CausedFocusEvent.Cause cause)
477   {
478     return false;
479   }
480 
isObscured()481   public boolean isObscured ()
482   {
483     return false;
484   }
485 
canDetermineObscurity()486   public boolean canDetermineObscurity ()
487   {
488     return false;
489   }
490 
coalescePaintEvent(PaintEvent e)491   public void coalescePaintEvent (PaintEvent e)
492   {
493   }
494 
updateCursorImmediately()495   public void updateCursorImmediately ()
496   {
497   }
498 
createVolatileImage(int width, int height)499   public VolatileImage createVolatileImage (int width, int height)
500   {
501     return null;
502   }
503 
handlesWheelScrolling()504   public boolean handlesWheelScrolling ()
505   {
506     return false;
507   }
508 
createBuffers(int x, BufferCapabilities capabilities)509   public void createBuffers (int x, BufferCapabilities capabilities)
510     throws java.awt.AWTException
511 
512   {
513   }
514 
getBackBuffer()515   public Image getBackBuffer ()
516   {
517     return null;
518   }
519 
flip(BufferCapabilities.FlipContents contents)520   public void flip (BufferCapabilities.FlipContents contents)
521   {
522   }
523 
destroyBuffers()524   public void destroyBuffers ()
525   {
526   }
527 
528   static class DoMap implements Runnable
529   {
530     Window window;
DoMap(Window w)531     public DoMap(Window w)
532     {
533       this.window = w;
534     }
535 
run()536     public void run()
537     {
538       window.map();
539     }
540   }
541 
542   /**
543    * @since 1.5
544    */
isRestackSupported()545   public boolean isRestackSupported ()
546   {
547     return false;
548   }
549 
550   /**
551    * @since 1.5
552    */
cancelPendingPaint(int x, int y, int width, int height)553   public void cancelPendingPaint (int x, int y, int width, int height)
554   {
555   }
556 
557   /**
558    * @since 1.5
559    */
restack()560   public void restack ()
561   {
562   }
563 
564   /**
565    * @since 1.5
566    */
getBounds()567   public Rectangle getBounds ()
568   {
569     return null;
570   }
571 
572   /**
573    * @since 1.5
574    */
reparent(ContainerPeer parent)575   public void reparent (ContainerPeer parent)
576   {
577   }
578 
579   /**
580    * @since 1.5
581    */
setBounds(int x, int y, int width, int height, int z)582   public void setBounds (int x, int y, int width, int height, int z)
583   {
584   }
585 
586   /**
587    * @since 1.5
588    */
isReparentSupported()589   public boolean isReparentSupported ()
590   {
591     return false;
592   }
593 
594   /**
595    * @since 1.5
596    */
layout()597   public void layout ()
598   {
599   }
600 }
601