1/*
2 * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package sun.awt.X11;
26
27import java.awt.AWTEvent;
28import java.awt.AWTException;
29import java.awt.BufferCapabilities;
30import java.awt.Color;
31import java.awt.Component;
32import java.awt.Container;
33import java.awt.Cursor;
34import java.awt.Dimension;
35import java.awt.Font;
36import java.awt.FontMetrics;
37import java.awt.Graphics;
38import java.awt.GraphicsConfiguration;
39import java.awt.Image;
40import java.awt.Insets;
41import java.awt.Rectangle;
42import java.awt.SystemColor;
43import java.awt.Toolkit;
44import java.awt.Window;
45import java.awt.dnd.DropTarget;
46import java.awt.dnd.peer.DropTargetPeer;
47import java.awt.event.FocusEvent;
48import java.awt.event.InputEvent;
49import java.awt.event.InputMethodEvent;
50import java.awt.event.KeyEvent;
51import java.awt.event.MouseEvent;
52import java.awt.event.MouseWheelEvent;
53import java.awt.event.PaintEvent;
54import java.awt.event.WindowEvent;
55import java.awt.image.ImageObserver;
56import java.awt.image.ImageProducer;
57import java.awt.image.VolatileImage;
58import java.awt.peer.ComponentPeer;
59import java.awt.peer.ContainerPeer;
60import java.util.Collection;
61import java.util.Objects;
62import java.util.Set;
63
64import sun.awt.AWTAccessor.ComponentAccessor;
65import sun.util.logging.PlatformLogger;
66import sun.awt.*;
67import sun.awt.event.IgnorePaintEvent;
68import sun.awt.image.SunVolatileImage;
69import sun.awt.image.ToolkitImage;
70import sun.java2d.BackBufferCapsProvider;
71import sun.java2d.pipe.Region;
72
73
74public class XComponentPeer extends XWindow implements ComponentPeer, DropTargetPeer,
75    BackBufferCapsProvider
76{
77    private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XComponentPeer");
78    private static final PlatformLogger buffersLog = PlatformLogger.getLogger("sun.awt.X11.XComponentPeer.multibuffer");
79    private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.X11.focus.XComponentPeer");
80    private static final PlatformLogger fontLog = PlatformLogger.getLogger("sun.awt.X11.font.XComponentPeer");
81    private static final PlatformLogger enableLog = PlatformLogger.getLogger("sun.awt.X11.enable.XComponentPeer");
82    private static final PlatformLogger shapeLog = PlatformLogger.getLogger("sun.awt.X11.shape.XComponentPeer");
83
84    boolean paintPending = false;
85    boolean isLayouting = false;
86    private boolean enabled;
87
88    // Actually used only by XDecoratedPeer
89    protected int boundsOperation;
90
91    Color foreground;
92    Color background;
93
94    // Colors calculated as on Motif using MotifColorUtilties.
95    // If you use these, call updateMotifColors() in the peer's Constructor and
96    // setBackground().  Examples are XCheckboxPeer and XButtonPeer.
97    Color darkShadow;
98    Color lightShadow;
99    Color selectColor;
100
101    Font font;
102    private long backBuffer = 0;
103    private VolatileImage xBackBuffer = null;
104
105    static Color[] systemColors;
106
107    XComponentPeer() {
108    }
109
110    XComponentPeer (XCreateWindowParams params) {
111        super(params);
112    }
113
114    XComponentPeer(Component target, long parentWindow, Rectangle bounds) {
115        super(target, parentWindow, bounds);
116    }
117
118    /**
119     * Standard peer constructor, with corresponding Component
120     */
121    XComponentPeer(Component target) {
122        super(target);
123    }
124
125
126    void preInit(XCreateWindowParams params) {
127        super.preInit(params);
128        boundsOperation = DEFAULT_OPERATION;
129    }
130    void postInit(XCreateWindowParams params) {
131        super.postInit(params);
132
133        pSetCursor(target.getCursor());
134
135        foreground = target.getForeground();
136        background = target.getBackground();
137        font = target.getFont();
138
139        if (isInitialReshape()) {
140            Rectangle r = target.getBounds();
141            reshape(r.x, r.y, r.width, r.height);
142        }
143
144        setEnabled(target.isEnabled());
145
146        if (target.isVisible()) {
147            setVisible(true);
148        }
149    }
150
151    protected boolean isInitialReshape() {
152        return true;
153    }
154
155    public void reparent(ContainerPeer newNativeParent) {
156        XComponentPeer newPeer = (XComponentPeer)newNativeParent;
157        XToolkit.awtLock();
158        try {
159            XlibWrapper.XReparentWindow(XToolkit.getDisplay(),
160                                        getWindow(), newPeer.getContentWindow(),
161                                        scaleUp(x), scaleUp(y));
162            parentWindow = newPeer;
163        } finally {
164            XToolkit.awtUnlock();
165        }
166    }
167    public boolean isReparentSupported() {
168        return System.getProperty("sun.awt.X11.XComponentPeer.reparentNotSupported", "false").equals("false");
169    }
170
171    @SuppressWarnings("deprecation")
172    public boolean isObscured() {
173        Container container  = (target instanceof Container) ?
174            (Container)target : target.getParent();
175
176        if (container == null) {
177            return true;
178        }
179
180        Container parent;
181        while ((parent = container.getParent()) != null) {
182            container = parent;
183        }
184
185        if (container instanceof Window) {
186            XWindowPeer wpeer = AWTAccessor.getComponentAccessor()
187                                           .getPeer(container);
188            if (wpeer != null) {
189                return (wpeer.winAttr.visibilityState !=
190                        XWindowAttributesData.AWT_UNOBSCURED);
191            }
192        }
193        return true;
194    }
195
196    public boolean canDetermineObscurity() {
197        return true;
198    }
199
200    /*************************************************
201     * FOCUS STUFF
202     *************************************************/
203
204    /**
205     * Keeps the track of focused state of the _NATIVE_ window
206     */
207    boolean bHasFocus = false;
208
209    /**
210     * Descendants should use this method to determine whether or not native window
211     * has focus.
212     */
213    public final boolean hasFocus() {
214        return bHasFocus;
215    }
216
217    /**
218     * Called when component receives focus
219     */
220    public void focusGained(FocusEvent e) {
221        if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
222            focusLog.fine("{0}", e);
223        }
224        bHasFocus = true;
225    }
226
227    /**
228     * Called when component loses focus
229     */
230    public void focusLost(FocusEvent e) {
231        if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
232            focusLog.fine("{0}", e);
233        }
234        bHasFocus = false;
235    }
236
237    public boolean isFocusable() {
238        /* should be implemented by other sub-classes */
239        return false;
240    }
241
242    static final AWTEvent wrapInSequenced(AWTEvent event) {
243        return AWTAccessor.getSequencedEventAccessor().create(event);
244    }
245
246    // TODO: consider moving it to KeyboardFocusManagerPeerImpl
247    @SuppressWarnings("deprecation")
248    public final boolean requestFocus(Component lightweightChild, boolean temporary,
249                                      boolean focusedWindowChangeAllowed, long time,
250                                      FocusEvent.Cause cause)
251    {
252        if (XKeyboardFocusManagerPeer.
253            processSynchronousLightweightTransfer(target, lightweightChild, temporary,
254                                                  focusedWindowChangeAllowed, time))
255        {
256            return true;
257        }
258
259        int result = XKeyboardFocusManagerPeer.
260            shouldNativelyFocusHeavyweight(target, lightweightChild,
261                                           temporary, focusedWindowChangeAllowed,
262                                           time, cause);
263
264        switch (result) {
265          case XKeyboardFocusManagerPeer.SNFH_FAILURE:
266              return false;
267          case XKeyboardFocusManagerPeer.SNFH_SUCCESS_PROCEED:
268              // Currently we just generate focus events like we deal with lightweight instead of calling
269              // XSetInputFocus on native window
270              if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
271                  focusLog.finer("Proceeding with request to " +
272                                 lightweightChild + " in " + target);
273              }
274              /**
275               * The problems with requests in non-focused window arise because shouldNativelyFocusHeavyweight
276               * checks that native window is focused while appropriate WINDOW_GAINED_FOCUS has not yet
277               * been processed - it is in EventQueue. Thus, SNFH allows native request and stores request record
278               * in requests list - and it breaks our requests sequence as first record on WGF should be the last
279               * focus owner which had focus before WLF. So, we should not add request record for such requests
280               * but store this component in mostRecent - and return true as before for compatibility.
281               */
282              Window parentWindow = SunToolkit.getContainingWindow(target);
283              if (parentWindow == null) {
284                  return rejectFocusRequestHelper("WARNING: Parent window is null");
285              }
286              XWindowPeer wpeer = AWTAccessor.getComponentAccessor()
287                                             .getPeer(parentWindow);
288              if (wpeer == null) {
289                  return rejectFocusRequestHelper("WARNING: Parent window's peer is null");
290              }
291              /*
292               * Passing null 'actualFocusedWindow' as we don't want to restore focus on it
293               * when a component inside a Frame is requesting focus.
294               * See 6314575 for details.
295               */
296              boolean res = wpeer.requestWindowFocus(null);
297
298              if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
299                  focusLog.finer("Requested window focus: " + res);
300              }
301              // If parent window can be made focused and has been made focused(synchronously)
302              // then we can proceed with children, otherwise we retreat.
303              if (!(res && parentWindow.isFocused())) {
304                  return rejectFocusRequestHelper("Waiting for asynchronous processing of the request");
305              }
306              return XKeyboardFocusManagerPeer.deliverFocus(lightweightChild,
307                                                            target,
308                                                            temporary,
309                                                            focusedWindowChangeAllowed,
310                                                            time, cause);
311              // Motif compatibility code
312          case XKeyboardFocusManagerPeer.SNFH_SUCCESS_HANDLED:
313              // Either lightweight or excessive request - all events are generated.
314              return true;
315        }
316        return false;
317    }
318
319    private boolean rejectFocusRequestHelper(String logMsg) {
320        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
321            focusLog.finer(logMsg);
322        }
323        XKeyboardFocusManagerPeer.removeLastFocusRequest(target);
324        return false;
325    }
326
327    void handleJavaFocusEvent(AWTEvent e) {
328        if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
329            focusLog.finer(e.toString());
330        }
331        if (e.getID() == FocusEvent.FOCUS_GAINED) {
332            focusGained((FocusEvent)e);
333        } else {
334            focusLost((FocusEvent)e);
335        }
336    }
337
338    void handleJavaWindowFocusEvent(AWTEvent e) {
339    }
340
341    /*************************************************
342     * END OF FOCUS STUFF
343     *************************************************/
344
345
346
347    public void setVisible(boolean b) {
348        xSetVisible(b);
349    }
350
351    public void hide() {
352        setVisible(false);
353    }
354
355    /**
356     * @see java.awt.peer.ComponentPeer
357     */
358    public void setEnabled(final boolean value) {
359        if (enableLog.isLoggable(PlatformLogger.Level.FINE)) {
360            enableLog.fine("{0}ing {1}", (value ? "Enabl" : "Disabl"), this);
361        }
362        boolean status = value;
363        // If any of our heavyweight ancestors are disable, we should be too
364        // See 6176875 for more information
365        final Container cp = SunToolkit.getNativeContainer(target);
366        final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
367        if (cp != null) {
368            status &= acc.<XComponentPeer>getPeer(cp).isEnabled();
369        }
370        synchronized (getStateLock()) {
371            if (enabled == status) {
372                return;
373            }
374            enabled = status;
375        }
376
377        if (target instanceof Container) {
378            final Component[] list = ((Container) target).getComponents();
379            for (final Component child : list) {
380                final ComponentPeer p = acc.getPeer(child);
381                if (p != null) {
382                    p.setEnabled(status && child.isEnabled());
383                }
384            }
385        }
386        repaint();
387    }
388
389    //
390    // public so aw/Window can call it
391    //
392    public final boolean isEnabled() {
393        synchronized (getStateLock()) {
394            return enabled;
395        }
396    }
397
398    @Override
399    public void paint(final Graphics g) {
400        super.paint(g);
401        // allow target to change the picture
402        target.paint(g);
403    }
404
405    public Graphics getGraphics() {
406        return getGraphics(surfaceData, getPeerForeground(), getPeerBackground(), getPeerFont());
407    }
408    public void print(Graphics g) {
409        // clear rect here to emulate X clears rect before Expose
410        g.setColor(target.getBackground());
411        g.fillRect(0, 0, target.getWidth(), target.getHeight());
412        g.setColor(target.getForeground());
413        // paint peer
414        paintPeer(g);
415        // allow target to change the picture
416        target.print(g);
417    }
418
419    public void setBounds(int x, int y, int width, int height, int op) {
420        this.x = x;
421        this.y = y;
422        this.width = width;
423        this.height = height;
424        xSetBounds(x,y,width,height);
425        validateSurface();
426        layout();
427    }
428
429    public void reshape(int x, int y, int width, int height) {
430        setBounds(x, y, width, height, SET_BOUNDS);
431    }
432
433    public void coalescePaintEvent(PaintEvent e) {
434        Rectangle r = e.getUpdateRect();
435        if (!(e instanceof IgnorePaintEvent)) {
436            paintArea.add(r, e.getID());
437        }
438        if (true) {
439            switch(e.getID()) {
440              case PaintEvent.UPDATE:
441                  if (log.isLoggable(PlatformLogger.Level.FINER)) {
442                      log.finer("XCP coalescePaintEvent : UPDATE : add : x = " +
443                            r.x + ", y = " + r.y + ", width = " + r.width + ",height = " + r.height);
444                  }
445                  return;
446              case PaintEvent.PAINT:
447                  if (log.isLoggable(PlatformLogger.Level.FINER)) {
448                      log.finer("XCP coalescePaintEvent : PAINT : add : x = " +
449                            r.x + ", y = " + r.y + ", width = " + r.width + ",height = " + r.height);
450                  }
451                  return;
452            }
453        }
454    }
455
456    XWindowPeer getParentTopLevel() {
457        ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
458        Container parent = (target instanceof Container) ? ((Container)target) : (compAccessor.getParent(target));
459        // Search for parent window
460        while (parent != null && !(parent instanceof Window)) {
461            parent = compAccessor.getParent(parent);
462        }
463        if (parent != null) {
464            return (XWindowPeer)compAccessor.getPeer(parent);
465        } else {
466            return null;
467        }
468    }
469
470    /* This method is intended to be over-ridden by peers to perform user interaction */
471    void handleJavaMouseEvent(MouseEvent e) {
472        switch (e.getID()) {
473          case MouseEvent.MOUSE_PRESSED:
474              if (target == e.getSource() &&
475                  !target.isFocusOwner() &&
476                  XKeyboardFocusManagerPeer.shouldFocusOnClick(target))
477              {
478                  XWindowPeer parentXWindow = getParentTopLevel();
479                  Window parentWindow = ((Window)parentXWindow.getTarget());
480                  // Simple windows are non-focusable in X terms but focusable in Java terms.
481                  // As X-non-focusable they don't receive any focus events - we should generate them
482                  // by ourselfves.
483//                   if (parentXWindow.isFocusableWindow() /*&& parentXWindow.isSimpleWindow()*/ &&
484//                       !(getCurrentNativeFocusedWindow() == parentWindow))
485//                   {
486//                       setCurrentNativeFocusedWindow(parentWindow);
487//                       WindowEvent wfg = new WindowEvent(parentWindow, WindowEvent.WINDOW_GAINED_FOCUS);
488//                       parentWindow.dispatchEvent(wfg);
489//                   }
490                  XKeyboardFocusManagerPeer.requestFocusFor(target, FocusEvent.Cause.MOUSE_EVENT);
491              }
492              break;
493        }
494    }
495
496    /* This method is intended to be over-ridden by peers to perform user interaction */
497    void handleJavaKeyEvent(KeyEvent e) {
498    }
499
500    /* This method is intended to be over-ridden by peers to perform user interaction */
501    void handleJavaMouseWheelEvent(MouseWheelEvent e) {
502    }
503
504
505    /* This method is intended to be over-ridden by peers to perform user interaction */
506    void handleJavaInputMethodEvent(InputMethodEvent e) {
507    }
508
509    void handleF10JavaKeyEvent(KeyEvent e) {
510        if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_F10) {
511            XWindowPeer winPeer = this.getToplevelXWindow();
512            if (winPeer instanceof XFramePeer) {
513                XMenuBarPeer mPeer = ((XFramePeer)winPeer).getMenubarPeer();
514                if (mPeer != null) {
515                    mPeer.handleF10KeyPress(e);
516                }
517            }
518        }
519    }
520
521    @SuppressWarnings("fallthrough")
522    public void handleEvent(java.awt.AWTEvent e) {
523        if ((e instanceof InputEvent) && !((InputEvent)e).isConsumed() && target.isEnabled())  {
524            if (e instanceof MouseEvent) {
525                if (e instanceof MouseWheelEvent) {
526                    handleJavaMouseWheelEvent((MouseWheelEvent) e);
527                }
528                else
529                    handleJavaMouseEvent((MouseEvent) e);
530            }
531            else if (e instanceof KeyEvent) {
532                handleF10JavaKeyEvent((KeyEvent)e);
533                handleJavaKeyEvent((KeyEvent)e);
534            }
535        }
536        else if (e instanceof KeyEvent && !((InputEvent)e).isConsumed()) {
537            // even if target is disabled.
538            handleF10JavaKeyEvent((KeyEvent)e);
539        }
540        else if (e instanceof InputMethodEvent) {
541            handleJavaInputMethodEvent((InputMethodEvent) e);
542        }
543
544        int id = e.getID();
545
546        switch(id) {
547          case PaintEvent.PAINT:
548              // Got native painting
549              paintPending = false;
550              // Fallthrough to next statement
551          case PaintEvent.UPDATE:
552              // Skip all painting while layouting and all UPDATEs
553              // while waiting for native paint
554              if (!isLayouting && !paintPending) {
555                  paintArea.paint(target,false);
556              }
557              return;
558          case FocusEvent.FOCUS_LOST:
559          case FocusEvent.FOCUS_GAINED:
560              handleJavaFocusEvent(e);
561              break;
562          case WindowEvent.WINDOW_LOST_FOCUS:
563          case WindowEvent.WINDOW_GAINED_FOCUS:
564              handleJavaWindowFocusEvent(e);
565              break;
566          default:
567              break;
568        }
569
570    }
571
572    public Dimension getMinimumSize() {
573        return target.getSize();
574    }
575
576    public Dimension getPreferredSize() {
577        return getMinimumSize();
578    }
579
580    public void layout() {}
581
582    void updateMotifColors(Color bg) {
583        int red = bg.getRed();
584        int green = bg.getGreen();
585        int blue = bg.getBlue();
586
587        darkShadow = new Color(MotifColorUtilities.calculateBottomShadowFromBackground(red,green,blue));
588        lightShadow = new Color(MotifColorUtilities.calculateTopShadowFromBackground(red,green,blue));
589        selectColor= new Color(MotifColorUtilities.calculateSelectFromBackground(red,green,blue));
590    }
591
592    /*
593     * Draw a 3D rectangle using the Motif colors.
594     * "Normal" rectangles have shadows on the bottom.
595     * "Depressed" rectangles (such as pressed buttons) have shadows on the top,
596     * in which case true should be passed for topShadow.
597     */
598    public void drawMotif3DRect(Graphics g,
599                                          int x, int y, int width, int height,
600                                          boolean topShadow) {
601        g.setColor(topShadow ? darkShadow : lightShadow);
602        g.drawLine(x, y, x+width, y);       // top
603        g.drawLine(x, y+height, x, y);      // left
604
605        g.setColor(topShadow ? lightShadow : darkShadow );
606        g.drawLine(x+1, y+height, x+width, y+height); // bottom
607        g.drawLine(x+width, y+height, x+width, y+1);  // right
608    }
609
610    @Override
611    public void setBackground(Color c) {
612        if (log.isLoggable(PlatformLogger.Level.FINE)) {
613            log.fine("Set background to " + c);
614        }
615        synchronized (getStateLock()) {
616            if (Objects.equals(background, c)) {
617                return;
618            }
619            background = c;
620        }
621        super.setBackground(c);
622        repaint();
623    }
624
625    @Override
626    public void setForeground(Color c) {
627        if (log.isLoggable(PlatformLogger.Level.FINE)) {
628            log.fine("Set foreground to " + c);
629        }
630        synchronized (getStateLock()) {
631            if (Objects.equals(foreground, c)) {
632                return;
633            }
634            foreground = c;
635        }
636        repaint();
637    }
638
639    /**
640     * Gets the font metrics for the specified font.
641     * @param font the font for which font metrics is to be
642     *      obtained
643     * @return the font metrics for {@code font}
644     * @see       #getFont
645     * @see       java.awt.peer.ComponentPeer#getFontMetrics(Font)
646     * @see       Toolkit#getFontMetrics(Font)
647     * @since     1.0
648     */
649    public FontMetrics getFontMetrics(Font font) {
650        if (fontLog.isLoggable(PlatformLogger.Level.FINE)) {
651            fontLog.fine("Getting font metrics for " + font);
652        }
653        return sun.font.FontDesignMetrics.getMetrics(font);
654    }
655
656    @Override
657    public void setFont(Font f) {
658        if (f == null) {
659            f = XWindow.getDefaultFont();
660        }
661        synchronized (getStateLock()) {
662            if (f.equals(font)) {
663                return;
664            }
665            font = f;
666        }
667        // as it stands currently we don't need to do layout since
668        // layout is done in the Component upon setFont.
669        //layout();
670        repaint();
671    }
672
673    public Font getFont() {
674        return font;
675    }
676
677    public void updateCursorImmediately() {
678        XGlobalCursorManager.getCursorManager().updateCursorImmediately();
679    }
680
681    public final void pSetCursor(Cursor cursor) {
682        this.pSetCursor(cursor, true);
683    }
684
685    /*
686     * The method changes the cursor.
687     * @param cursor  a new cursor to change to.
688     * @param ignoreSubComponents   if {@code true} is passed then
689     *                              the new cursor will be installed on window.
690     *                              if {@code false} is passed then
691     *                              subsequent components will try to handle
692     *                              this request and install their cursor.
693     */
694    //ignoreSubComponents not used here
695    public void pSetCursor(Cursor cursor, boolean ignoreSubComponents) {
696        XToolkit.awtLock();
697        try {
698            long xcursor = XGlobalCursorManager.getCursor(cursor);
699
700            XSetWindowAttributes xwa = new XSetWindowAttributes();
701            xwa.set_cursor(xcursor);
702
703            long valuemask = XConstants.CWCursor;
704
705            XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),getWindow(),valuemask,xwa.pData);
706            XlibWrapper.XFlush(XToolkit.getDisplay());
707            xwa.dispose();
708        } finally {
709            XToolkit.awtUnlock();
710        }
711    }
712
713    public Image createImage(ImageProducer producer) {
714        return new ToolkitImage(producer);
715    }
716
717    public Image createImage(int width, int height) {
718        return graphicsConfig.createAcceleratedImage(target, width, height);
719    }
720
721    public VolatileImage createVolatileImage(int width, int height) {
722        return new SunVolatileImage(target, width, height);
723    }
724
725    public boolean prepareImage(Image img, int w, int h, ImageObserver o) {
726        return Toolkit.getDefaultToolkit().prepareImage(img, w, h, o);
727    }
728
729    public int checkImage(Image img, int w, int h, ImageObserver o) {
730        return Toolkit.getDefaultToolkit().checkImage(img, w, h, o);
731    }
732
733    public Dimension preferredSize() {
734        return getPreferredSize();
735    }
736
737    public Dimension minimumSize() {
738        return getMinimumSize();
739    }
740
741    public Insets getInsets() {
742        return new Insets(0, 0, 0, 0);
743    }
744
745    public void beginValidate() {
746    }
747
748    public void endValidate() {
749    }
750
751
752    /**
753     * DEPRECATED:  Replaced by getInsets().
754     */
755
756    public Insets insets() {
757        return getInsets();
758    }
759
760    // Returns true if we are inside begin/endLayout and
761    // are waiting for native painting
762    public boolean isPaintPending() {
763        return paintPending && isLayouting;
764    }
765
766    public boolean handlesWheelScrolling() {
767        return false;
768    }
769
770    public void beginLayout() {
771        // Skip all painting till endLayout
772        isLayouting = true;
773
774    }
775
776    public void endLayout() {
777        if (!paintPending && !paintArea.isEmpty()
778            && !AWTAccessor.getComponentAccessor().getIgnoreRepaint(target))
779        {
780            // if not waiting for native painting repaint damaged area
781            postEvent(new PaintEvent(target, PaintEvent.PAINT,
782                                     new Rectangle()));
783        }
784        isLayouting = false;
785    }
786
787    public Color getWinBackground() {
788        return getPeerBackground();
789    }
790
791    static int[] getRGBvals(Color c) {
792
793        int rgbvals[] = new int[3];
794
795        rgbvals[0] = c.getRed();
796        rgbvals[1] = c.getGreen();
797        rgbvals[2] = c.getBlue();
798
799        return rgbvals;
800    }
801
802    static final int BACKGROUND_COLOR = 0;
803    static final int HIGHLIGHT_COLOR = 1;
804    static final int SHADOW_COLOR = 2;
805    static final int FOREGROUND_COLOR = 3;
806
807    public Color[] getGUIcolors() {
808        Color c[] = new Color[4];
809        float backb, highb, shadowb, hue, saturation;
810        c[BACKGROUND_COLOR] = getWinBackground();
811        if (c[BACKGROUND_COLOR] == null) {
812            c[BACKGROUND_COLOR] = super.getWinBackground();
813        }
814        if (c[BACKGROUND_COLOR] == null) {
815            c[BACKGROUND_COLOR] = Color.lightGray;
816        }
817
818        int[] rgb = getRGBvals(c[BACKGROUND_COLOR]);
819
820        float[] hsb = Color.RGBtoHSB(rgb[0],rgb[1],rgb[2],null);
821
822        hue = hsb[0];
823        saturation = hsb[1];
824        backb = hsb[2];
825
826
827/*      Calculate Highlight Brightness  */
828
829        highb = backb + 0.2f;
830        shadowb = backb - 0.4f;
831        if ((highb > 1.0) ) {
832            if  ((1.0 - backb) < 0.05) {
833                highb = shadowb + 0.25f;
834            } else {
835                highb = 1.0f;
836            }
837        } else {
838            if (shadowb < 0.0) {
839                if ((backb - 0.0) < 0.25) {
840                    highb = backb + 0.75f;
841                    shadowb = highb - 0.2f;
842                } else {
843                    shadowb = 0.0f;
844                }
845            }
846        }
847        c[HIGHLIGHT_COLOR] = Color.getHSBColor(hue,saturation,highb);
848        c[SHADOW_COLOR] = Color.getHSBColor(hue,saturation,shadowb);
849
850
851/*
852  c[SHADOW_COLOR] = c[BACKGROUND_COLOR].darker();
853  int r2 = c[SHADOW_COLOR].getRed();
854  int g2 = c[SHADOW_COLOR].getGreen();
855  int b2 = c[SHADOW_COLOR].getBlue();
856*/
857
858        c[FOREGROUND_COLOR] = getPeerForeground();
859        if (c[FOREGROUND_COLOR] == null) {
860            c[FOREGROUND_COLOR] = Color.black;
861        }
862/*
863  if ((c[BACKGROUND_COLOR].equals(c[HIGHLIGHT_COLOR]))
864  && (c[BACKGROUND_COLOR].equals(c[SHADOW_COLOR]))) {
865  c[SHADOW_COLOR] = new Color(c[BACKGROUND_COLOR].getRed() + 75,
866  c[BACKGROUND_COLOR].getGreen() + 75,
867  c[BACKGROUND_COLOR].getBlue() + 75);
868  c[HIGHLIGHT_COLOR] = c[SHADOW_COLOR].brighter();
869  } else if (c[BACKGROUND_COLOR].equals(c[HIGHLIGHT_COLOR])) {
870  c[HIGHLIGHT_COLOR] = c[SHADOW_COLOR];
871  c[SHADOW_COLOR] = c[SHADOW_COLOR].darker();
872  }
873*/
874        if (! isEnabled()) {
875            c[BACKGROUND_COLOR] = c[BACKGROUND_COLOR].darker();
876            // Reduce the contrast
877            // Calculate the NTSC gray (NB: REC709 L* might be better!)
878            // for foreground and background; then multiply the foreground
879            // by the average lightness
880
881
882            Color tc = c[BACKGROUND_COLOR];
883            int bg = tc.getRed() * 30 + tc.getGreen() * 59 + tc.getBlue() * 11;
884
885            tc = c[FOREGROUND_COLOR];
886            int fg = tc.getRed() * 30 + tc.getGreen() * 59 + tc.getBlue() * 11;
887
888            float ave = (float) ((fg + bg) / 51000.0);
889            // 255 * 100 * 2
890
891            Color newForeground = new Color((int) (tc.getRed() * ave),
892                                            (int) (tc.getGreen() * ave),
893                                            (int) (tc.getBlue() * ave));
894
895            if (newForeground.equals(c[FOREGROUND_COLOR])) {
896                // This probably means the foreground color is black or white
897                newForeground = new Color(ave, ave, ave);
898            }
899            c[FOREGROUND_COLOR] = newForeground;
900
901        }
902
903
904        return c;
905    }
906
907    /**
908     * Returns an array of Colors similar to getGUIcolors(), but using the
909     * System colors.  This is useful if pieces of a Component (such as
910     * the integrated scrollbars of a List) should retain the System color
911     * instead of the background color set by Component.setBackground().
912     */
913    static Color[] getSystemColors() {
914        if (systemColors == null) {
915            systemColors = new Color[4];
916            systemColors[BACKGROUND_COLOR] = SystemColor.window;
917            systemColors[HIGHLIGHT_COLOR] = SystemColor.controlLtHighlight;
918            systemColors[SHADOW_COLOR] = SystemColor.controlShadow;
919            systemColors[FOREGROUND_COLOR] = SystemColor.windowText;
920        }
921        return systemColors;
922    }
923
924    /**
925     * Draw a 3D oval.
926     */
927    public void draw3DOval(Graphics g, Color colors[],
928                           int x, int y, int w, int h, boolean raised)
929        {
930        Color c = g.getColor();
931        g.setColor(raised ? colors[HIGHLIGHT_COLOR] : colors[SHADOW_COLOR]);
932        g.drawArc(x, y, w, h, 45, 180);
933        g.setColor(raised ? colors[SHADOW_COLOR] : colors[HIGHLIGHT_COLOR]);
934        g.drawArc(x, y, w, h, 225, 180);
935        g.setColor(c);
936    }
937
938    public void draw3DRect(Graphics g, Color colors[],
939                           int x, int y, int width, int height, boolean raised)
940        {
941            Color c = g.getColor();
942            g.setColor(raised ? colors[HIGHLIGHT_COLOR] : colors[SHADOW_COLOR]);
943            g.drawLine(x, y, x, y + height);
944            g.drawLine(x + 1, y, x + width - 1, y);
945            g.setColor(raised ? colors[SHADOW_COLOR] : colors[HIGHLIGHT_COLOR]);
946            g.drawLine(x + 1, y + height, x + width, y + height);
947            g.drawLine(x + width, y, x + width, y + height - 1);
948            g.setColor(c);
949        }
950
951    /*
952     * drawXXX() methods are used to print the native components by
953     * rendering the Motif look ourselves.
954     * ToDo(aim): needs to query native motif for more accurate color
955     * information.
956     */
957    void draw3DOval(Graphics g, Color bg,
958                    int x, int y, int w, int h, boolean raised)
959        {
960            Color c = g.getColor();
961            Color shadow = bg.darker();
962            Color highlight = bg.brighter();
963
964            g.setColor(raised ? highlight : shadow);
965            g.drawArc(x, y, w, h, 45, 180);
966            g.setColor(raised ? shadow : highlight);
967            g.drawArc(x, y, w, h, 225, 180);
968            g.setColor(c);
969        }
970
971    void draw3DRect(Graphics g, Color bg,
972                    int x, int y, int width, int height,
973                    boolean raised) {
974        Color c = g.getColor();
975        Color shadow = bg.darker();
976        Color highlight = bg.brighter();
977
978        g.setColor(raised ? highlight : shadow);
979        g.drawLine(x, y, x, y + height);
980        g.drawLine(x + 1, y, x + width - 1, y);
981        g.setColor(raised ? shadow : highlight);
982        g.drawLine(x + 1, y + height, x + width, y + height);
983        g.drawLine(x + width, y, x + width, y + height - 1);
984        g.setColor(c);
985    }
986
987    void drawScrollbar(Graphics g, Color bg, int thickness, int length,
988               int min, int max, int val, int vis, boolean horizontal) {
989        Color c = g.getColor();
990        double f = (double)(length - 2*(thickness-1)) / Math.max(1, ((max - min) + vis));
991        int v1 = thickness + (int)(f * (val - min));
992        int v2 = (int)(f * vis);
993        int w2 = thickness-4;
994        int tpts_x[] = new int[3];
995        int tpts_y[] = new int[3];
996
997        if (length < 3*w2 ) {
998            v1 = v2 = 0;
999            if (length < 2*w2 + 2) {
1000                w2 = (length-2)/2;
1001            }
1002        } else  if (v2 < 7) {
1003            // enforce a minimum handle size
1004            v1 = Math.max(0, v1 - ((7 - v2)>>1));
1005            v2 = 7;
1006        }
1007
1008        int ctr   = thickness/2;
1009        int sbmin = ctr - w2/2;
1010        int sbmax = ctr + w2/2;
1011
1012        // paint the background slightly darker
1013        {
1014            Color d = new Color((int) (bg.getRed()   * 0.85),
1015                                (int) (bg.getGreen() * 0.85),
1016                                (int) (bg.getBlue()  * 0.85));
1017
1018            g.setColor(d);
1019            if (horizontal) {
1020                g.fillRect(0, 0, length, thickness);
1021            } else {
1022                g.fillRect(0, 0, thickness, length);
1023            }
1024        }
1025
1026        // paint the thumb and arrows in the normal background color
1027        g.setColor(bg);
1028        if (v1 > 0) {
1029            if (horizontal) {
1030                g.fillRect(v1, 3, v2, thickness-3);
1031            } else {
1032                g.fillRect(3, v1, thickness-3, v2);
1033            }
1034        }
1035
1036        tpts_x[0] = ctr;    tpts_y[0] = 2;
1037        tpts_x[1] = sbmin;  tpts_y[1] = w2;
1038        tpts_x[2] = sbmax;  tpts_y[2] = w2;
1039        if (horizontal) {
1040            g.fillPolygon(tpts_y, tpts_x, 3);
1041        } else {
1042            g.fillPolygon(tpts_x, tpts_y, 3);
1043        }
1044
1045        tpts_y[0] = length-2;
1046        tpts_y[1] = length-w2;
1047        tpts_y[2] = length-w2;
1048        if (horizontal) {
1049            g.fillPolygon(tpts_y, tpts_x, 3);
1050        } else {
1051            g.fillPolygon(tpts_x, tpts_y, 3);
1052        }
1053
1054        Color highlight = bg.brighter();
1055
1056        // // // // draw the "highlighted" edges
1057        g.setColor(highlight);
1058
1059        // outline & arrows
1060        if (horizontal) {
1061            g.drawLine(1, thickness, length - 1, thickness);
1062            g.drawLine(length - 1, 1, length - 1, thickness);
1063
1064            // arrows
1065            g.drawLine(1, ctr, w2, sbmin);
1066            g.drawLine(length - w2, sbmin, length - w2, sbmax);
1067            g.drawLine(length - w2, sbmin, length - 2, ctr);
1068
1069        } else {
1070            g.drawLine(thickness, 1, thickness, length - 1);
1071            g.drawLine(1, length - 1, thickness, length - 1);
1072
1073            // arrows
1074            g.drawLine(ctr, 1, sbmin, w2);
1075            g.drawLine(sbmin, length - w2, sbmax, length - w2);
1076            g.drawLine(sbmin, length - w2, ctr, length - 2);
1077        }
1078
1079        // thumb
1080        if (v1 > 0) {
1081            if (horizontal) {
1082                g.drawLine(v1, 2, v1 + v2, 2);
1083                g.drawLine(v1, 2, v1, thickness-3);
1084            } else {
1085                g.drawLine(2, v1, 2, v1 + v2);
1086                g.drawLine(2, v1, thickness-3, v1);
1087            }
1088        }
1089
1090        Color shadow = bg.darker();
1091
1092        // // // // draw the "shadowed" edges
1093        g.setColor(shadow);
1094
1095        // outline && arrows
1096        if (horizontal) {
1097            g.drawLine(0, 0, 0, thickness);
1098            g.drawLine(0, 0, length - 1, 0);
1099
1100            // arrows
1101            g.drawLine(w2, sbmin, w2, sbmax);
1102            g.drawLine(w2, sbmax, 1, ctr);
1103            g.drawLine(length-2, ctr, length-w2, sbmax);
1104
1105        } else {
1106            g.drawLine(0, 0, thickness, 0);
1107            g.drawLine(0, 0, 0, length - 1);
1108
1109            // arrows
1110            g.drawLine(sbmin, w2, sbmax, w2);
1111            g.drawLine(sbmax, w2, ctr, 1);
1112            g.drawLine(ctr, length-2, sbmax, length-w2);
1113        }
1114
1115        // thumb
1116        if (v1 > 0) {
1117            if (horizontal) {
1118                g.drawLine(v1 + v2, 2, v1 + v2, thickness-2);
1119                g.drawLine(v1, thickness-2, v1 + v2, thickness-2);
1120            } else {
1121                g.drawLine(2, v1 + v2, thickness-2, v1 + v2);
1122                g.drawLine(thickness-2, v1, thickness-2, v1 + v2);
1123            }
1124        }
1125        g.setColor(c);
1126    }
1127
1128    /**
1129     * The following multibuffering-related methods delegate to our
1130     * associated GraphicsConfig (X11 or GLX) to handle the appropriate
1131     * native windowing system specific actions.
1132     */
1133
1134    private BufferCapabilities backBufferCaps;
1135
1136    public void createBuffers(int numBuffers, BufferCapabilities caps)
1137      throws AWTException
1138    {
1139        if (buffersLog.isLoggable(PlatformLogger.Level.FINE)) {
1140            buffersLog.fine("createBuffers(" + numBuffers + ", " + caps + ")");
1141        }
1142        // set the caps first, they're used when creating the bb
1143        backBufferCaps = caps;
1144        backBuffer = graphicsConfig.createBackBuffer(this, numBuffers, caps);
1145        xBackBuffer = graphicsConfig.createBackBufferImage(target,
1146                                                           backBuffer);
1147    }
1148
1149    @Override
1150    public BufferCapabilities getBackBufferCaps() {
1151        return backBufferCaps;
1152    }
1153
1154    public void flip(int x1, int y1, int x2, int y2,
1155                     BufferCapabilities.FlipContents flipAction)
1156    {
1157        if (buffersLog.isLoggable(PlatformLogger.Level.FINE)) {
1158            buffersLog.fine("flip(" + flipAction + ")");
1159        }
1160        if (backBuffer == 0) {
1161            throw new IllegalStateException("Buffers have not been created");
1162        }
1163        graphicsConfig.flip(this, target, xBackBuffer,
1164                            x1, y1, x2, y2, flipAction);
1165    }
1166
1167    public Image getBackBuffer() {
1168        if (buffersLog.isLoggable(PlatformLogger.Level.FINE)) {
1169            buffersLog.fine("getBackBuffer()");
1170        }
1171        if (backBuffer == 0) {
1172            throw new IllegalStateException("Buffers have not been created");
1173        }
1174        return xBackBuffer;
1175    }
1176
1177    public void destroyBuffers() {
1178        if (buffersLog.isLoggable(PlatformLogger.Level.FINE)) {
1179            buffersLog.fine("destroyBuffers()");
1180        }
1181        graphicsConfig.destroyBackBuffer(backBuffer);
1182        backBuffer = 0;
1183        xBackBuffer = null;
1184    }
1185
1186    // End of multi-buffering
1187
1188    public void notifyTextComponentChange(boolean add){
1189        Container parent = AWTAccessor.getComponentAccessor().getParent(target);
1190        while(!(parent == null ||
1191                parent instanceof java.awt.Frame ||
1192                parent instanceof java.awt.Dialog)) {
1193            parent = AWTAccessor.getComponentAccessor().getParent(parent);
1194        }
1195
1196/*      FIX ME - FIX ME need to implement InputMethods
1197    if (parent instanceof java.awt.Frame ||
1198        parent instanceof java.awt.Dialog) {
1199        if (add)
1200        ((MInputMethodControl)parent.getPeer()).addTextComponent((MComponentPeer)this);
1201        else
1202        ((MInputMethodControl)parent.getPeer()).removeTextComponent((MComponentPeer)this);
1203    }
1204*/
1205    }
1206
1207    /**
1208     * Returns true if this event is disabled and shouldn't be processed by window
1209     * Currently if target component is disabled the following event will be disabled on window:
1210     * ButtonPress, ButtonRelease, KeyPress, KeyRelease, EnterNotify, LeaveNotify, MotionNotify
1211     */
1212    protected boolean isEventDisabled(XEvent e) {
1213        if (enableLog.isLoggable(PlatformLogger.Level.FINEST)) {
1214            enableLog.finest("Component is {1}, checking for disabled event {0}", e, (isEnabled()?"enabled":"disable"));
1215        }
1216        if (!isEnabled()) {
1217            switch (e.get_type()) {
1218              case XConstants.ButtonPress:
1219              case XConstants.ButtonRelease:
1220              case XConstants.KeyPress:
1221              case XConstants.KeyRelease:
1222              case XConstants.EnterNotify:
1223              case XConstants.LeaveNotify:
1224              case XConstants.MotionNotify:
1225                  if (enableLog.isLoggable(PlatformLogger.Level.FINER)) {
1226                      enableLog.finer("Event {0} is disable", e);
1227                  }
1228                  return true;
1229            }
1230        }
1231        switch(e.get_type()) {
1232          case XConstants.MapNotify:
1233          case XConstants.UnmapNotify:
1234              return true;
1235        }
1236        return super.isEventDisabled(e);
1237    }
1238
1239    Color getPeerBackground() {
1240        return background;
1241    }
1242
1243    Color getPeerForeground() {
1244        return foreground;
1245    }
1246
1247    Font getPeerFont() {
1248        return font;
1249    }
1250
1251    Dimension getPeerSize() {
1252        return new Dimension(width,height);
1253    }
1254
1255    public void setBoundsOperation(int operation) {
1256        synchronized(getStateLock()) {
1257            if (boundsOperation == DEFAULT_OPERATION) {
1258                boundsOperation = operation;
1259            } else if (operation == RESET_OPERATION) {
1260                boundsOperation = DEFAULT_OPERATION;
1261            }
1262        }
1263    }
1264
1265    static String operationToString(int operation) {
1266        switch (operation) {
1267          case SET_LOCATION:
1268              return "SET_LOCATION";
1269          case SET_SIZE:
1270              return "SET_SIZE";
1271          case SET_CLIENT_SIZE:
1272              return "SET_CLIENT_SIZE";
1273          default:
1274          case SET_BOUNDS:
1275              return "SET_BOUNDS";
1276        }
1277    }
1278
1279    /**
1280     * Lowers this component at the bottom of the above HW peer. If the above parameter
1281     * is null then the method places this component at the top of the Z-order.
1282     */
1283    public void setZOrder(ComponentPeer above) {
1284        long aboveWindow = (above != null) ? ((XComponentPeer)above).getWindow() : 0;
1285
1286        XToolkit.awtLock();
1287        try{
1288            XlibWrapper.SetZOrder(XToolkit.getDisplay(), getWindow(), aboveWindow);
1289        }finally{
1290            XToolkit.awtUnlock();
1291        }
1292    }
1293
1294    private void addTree(Collection<Long> order, Set<Long> set, Container cont) {
1295        for (int i = 0; i < cont.getComponentCount(); i++) {
1296            Component comp = cont.getComponent(i);
1297            Object peer = AWTAccessor.getComponentAccessor().getPeer(comp);
1298            if (peer instanceof XComponentPeer) {
1299                Long window = Long.valueOf(((XComponentPeer)peer).getWindow());
1300                if (!set.contains(window)) {
1301                    set.add(window);
1302                    order.add(window);
1303                }
1304            } else if (comp instanceof Container) {
1305                // It is lightweight container, it might contain heavyweight components attached to this
1306                // peer
1307                addTree(order, set, (Container)comp);
1308            }
1309        }
1310    }
1311
1312    /****** DropTargetPeer implementation ********************/
1313
1314    public void addDropTarget(DropTarget dt) {
1315        Component comp = target;
1316        while(!(comp == null || comp instanceof Window)) {
1317            comp = comp.getParent();
1318        }
1319
1320        if (comp instanceof Window) {
1321            XWindowPeer wpeer = AWTAccessor.getComponentAccessor().getPeer(comp);
1322            if (wpeer != null) {
1323                wpeer.addDropTarget();
1324            }
1325        }
1326    }
1327
1328    public void removeDropTarget(DropTarget dt) {
1329        Component comp = target;
1330        while(!(comp == null || comp instanceof Window)) {
1331            comp = comp.getParent();
1332        }
1333
1334        if (comp instanceof Window) {
1335            XWindowPeer wpeer = AWTAccessor.getComponentAccessor()
1336                                           .getPeer(comp);
1337            if (wpeer != null) {
1338                wpeer.removeDropTarget();
1339            }
1340        }
1341    }
1342
1343    /**
1344     * Applies the shape to the X-window.
1345     * @since 1.7
1346     */
1347    public void applyShape(Region shape) {
1348        if (XlibUtil.isShapingSupported()) {
1349            if (shapeLog.isLoggable(PlatformLogger.Level.FINER)) {
1350                shapeLog.finer(
1351                        "*** INFO: Setting shape: PEER: " + this
1352                        + "; WINDOW: " + getWindow()
1353                        + "; TARGET: " + target
1354                        + "; SHAPE: " + shape);
1355            }
1356            XToolkit.awtLock();
1357            try {
1358                if (shape != null) {
1359
1360                    int scale = getScale();
1361                    if (scale != 1) {
1362                        shape = shape.getScaledRegion(scale, scale);
1363                    }
1364
1365                    XlibWrapper.SetRectangularShape(
1366                            XToolkit.getDisplay(),
1367                            getWindow(),
1368                            shape.getLoX(), shape.getLoY(),
1369                            shape.getHiX(), shape.getHiY(),
1370                            (shape.isRectangular() ? null : shape)
1371                            );
1372                } else {
1373                    XlibWrapper.SetRectangularShape(
1374                            XToolkit.getDisplay(),
1375                            getWindow(),
1376                            0, 0,
1377                            0, 0,
1378                            null
1379                            );
1380                }
1381            } finally {
1382                XToolkit.awtUnlock();
1383            }
1384        } else {
1385            if (shapeLog.isLoggable(PlatformLogger.Level.FINER)) {
1386                shapeLog.finer("*** WARNING: Shaping is NOT supported!");
1387            }
1388        }
1389    }
1390
1391    public boolean updateGraphicsData(GraphicsConfiguration gc) {
1392        int oldVisual = -1, newVisual = -1;
1393
1394        if (graphicsConfig != null) {
1395            oldVisual = graphicsConfig.getVisual();
1396        }
1397        if (gc != null && gc instanceof X11GraphicsConfig) {
1398            newVisual = ((X11GraphicsConfig)gc).getVisual();
1399        }
1400
1401        // If the new visual differs from the old one, the peer must be
1402        // recreated because X11 does not allow changing the visual on the fly.
1403        // So we even skip the initGraphicsConfiguration() call.
1404        // The initial assignment should happen though, hence the != -1 thing.
1405        if (oldVisual != -1 && oldVisual != newVisual) {
1406            return true;
1407        }
1408
1409        initGraphicsConfiguration();
1410        doValidateSurface();
1411        return false;
1412    }
1413}
1414