1 /*
   2  * Copyright (c) 2003, 2013, 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  */
  25 
  26  /*
  27    * This code is ported to XAWT from MAWT based on awt_mgrsel.c
  28    * code written originally by Valeriy Ushakov
  29    * Author : Bino George
  30    */
  31 
  32 package sun.awt.X11;
  33 
  34 import java.util.*;
  35 import sun.util.logging.PlatformLogger;
  36 
  37 public class  XMSelection {
  38 
  39     /*
  40      * A method for a subsytem to express its interest in a certain
  41      * manager selection.
  42      *
  43      * If owner changes, the ownerChanged of the XMSelectionListener
  44      * will be called with the screen
  45      * number and the new owning window when onwership is established, or
  46      * None if the owner is gone.
  47      *
  48      * Events in extra_mask are selected for on owning windows (exsiting
  49      * ones and on new owners when established) and otherEvent of the
  50      * XMWSelectionListener will be called with the screen number and an event.
  51      *
  52      * The function returns an array of current owners.  The size of the
  53      * array is ScreenCount(awt_display).  The array is "owned" by this
  54      * module and should be considered by the caller as read-only.
  55      */
  56 
  57 
  58     private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XMSelection");
  59     /* Name of the selection */
  60     String selectionName;
  61 
  62     /* list of listeners to be called for events */
  63     Vector<XMSelectionListener> listeners;
  64 
  65     /* X atom array (one per screen) for this selection */
  66     XAtom atoms[];
  67 
  68     /* Window ids of selection owners */
  69     long owners[];
  70 
  71     /* event mask to set */
  72     long eventMask;
  73 
  74     static int numScreens;
  75 
  76     static XAtom XA_MANAGER;
  77 
  78     static HashMap<Long, XMSelection> selectionMap;
  79 
  80     static {
  81         long display = XToolkit.getDisplay();
  82         XToolkit.awtLock();
  83         try {
  84             numScreens = XlibWrapper.ScreenCount(display);
  85         } finally {
  86             XToolkit.awtUnlock();
  87         }
  88         XA_MANAGER = XAtom.get("MANAGER");
  89         for (int screen = 0; screen < numScreens ; screen ++) {
  90             initScreen(display,screen);
  91         }
  92 
  93         selectionMap = new HashMap<>();
  94     }
  95 
  96     static void initScreen(long display, final int screen) {
  97         XToolkit.awtLock();
  98         try {
  99             long root = XlibWrapper.RootWindow(display,screen);
 100             XWindowAttributes wattr = new XWindowAttributes();
 101             try{
 102                 XlibWrapper.XGetWindowAttributes(display, root, wattr.pData);
 103                 XlibWrapper.XSelectInput(display, root,
 104                         XConstants.StructureNotifyMask |
 105                         wattr.get_your_event_mask());
 106             } finally {
 107                 wattr.dispose();
 108             }
 109             XToolkit.addEventDispatcher(root,
 110                     new XEventDispatcher() {
 111                         public void dispatchEvent(XEvent ev) {
 112                                 processRootEvent(ev, screen);
 113                             }
 114                         });
 115 
 116         } finally {
 117             XToolkit.awtUnlock();
 118         }
 119     }
 120 
 121 
 122     public int getNumberOfScreens() {
 123         return numScreens;
 124     }
 125 
 126     void select(long extra_mask) {
 127         eventMask = extra_mask;
 128         for (int screen = 0; screen < numScreens ; screen ++) {
 129             selectPerScreen(screen,extra_mask);
 130         }
 131     }
 132 
 133     void resetOwner(long owner, final int screen) {
 134         XToolkit.awtLock();
 135         try {
 136             long display = XToolkit.getDisplay();
 137             synchronized(this) {
 138                 setOwner(owner, screen);
 139                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
 140                     log.fine("New Selection Owner for screen " + screen + " = " + owner );
 141                 }
 142                 XlibWrapper.XSelectInput(display, owner, XConstants.StructureNotifyMask | eventMask);
 143                 XToolkit.addEventDispatcher(owner,
 144                         new XEventDispatcher() {
 145                             public void dispatchEvent(XEvent ev) {
 146                                 dispatchSelectionEvent(ev, screen);
 147                             }
 148                         });
 149 
 150             }
 151         } finally {
 152             XToolkit.awtUnlock();
 153         }
 154     }
 155 
 156     void selectPerScreen(final int screen, long extra_mask) {
 157         XToolkit.awtLock();
 158         try {
 159             try {
 160                 long display = XToolkit.getDisplay();
 161                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
 162                     log.fine("Grabbing XServer");
 163                 }
 164                 XlibWrapper.XGrabServer(display);
 165 
 166                 synchronized(this) {
 167                     String selection_name = getName()+"_S"+screen;
 168                     if (log.isLoggable(PlatformLogger.Level.FINE)) {
 169                         log.fine("Screen = " + screen + " selection name = " + selection_name);
 170                     }
 171                     XAtom atom = XAtom.get(selection_name);
 172                     selectionMap.put(Long.valueOf(atom.getAtom()),this); // add mapping from atom to the instance of XMSelection
 173                     setAtom(atom,screen);
 174                     long owner = XlibWrapper.XGetSelectionOwner(display, atom.getAtom());
 175                     if (owner != 0) {
 176                         setOwner(owner, screen);
 177                         if (log.isLoggable(PlatformLogger.Level.FINE)) {
 178                             log.fine("Selection Owner for screen " + screen + " = " + owner );
 179                         }
 180                         XlibWrapper.XSelectInput(display, owner, XConstants.StructureNotifyMask | extra_mask);
 181                         XToolkit.addEventDispatcher(owner,
 182                                 new XEventDispatcher() {
 183                                         public void dispatchEvent(XEvent ev) {
 184                                             dispatchSelectionEvent(ev, screen);
 185                                         }
 186                                     });
 187                     }
 188                 }
 189             }
 190             catch (Exception e) {
 191                 e.printStackTrace();
 192             }
 193             finally {
 194                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
 195                     log.fine("UnGrabbing XServer");
 196                 }
 197                 XlibWrapper.XUngrabServer(XToolkit.getDisplay());
 198             }
 199         } finally {
 200             XToolkit.awtUnlock();
 201         }
 202     }
 203 
 204 
 205     static boolean processClientMessage(XEvent xev, int screen) {
 206         XClientMessageEvent xce = xev.get_xclient();
 207         if (xce.get_message_type() == XA_MANAGER.getAtom()) {
 208             if (log.isLoggable(PlatformLogger.Level.FINE)) {
 209                 log.fine("client messags = " + xce);
 210             }
 211             long timestamp = xce.get_data(0);
 212             long atom = xce.get_data(1);
 213             long owner = xce.get_data(2);
 214             long data = xce.get_data(3);
 215 
 216             XMSelection sel = getInstance(atom);
 217             if (sel != null) {
 218                 sel.resetOwner(owner,screen);
 219                 sel.dispatchOwnerChangedEvent(xev,screen,owner,data, timestamp);
 220             }
 221         }
 222         return false;
 223     }
 224 
 225     static  boolean processRootEvent(XEvent xev, int screen) {
 226         switch (xev.get_type()) {
 227             case XConstants.ClientMessage: {
 228                 return processClientMessage(xev, screen);
 229             }
 230         }
 231 
 232         return false;
 233 
 234     }
 235 
 236 
 237     static XMSelection getInstance(long selection) {
 238         return selectionMap.get(Long.valueOf(selection));
 239     }
 240 
 241 
 242     /*
 243      * Default constructor specifies PropertyChangeMask as well
 244      */
 245 
 246     public XMSelection (String selname) {
 247         this(selname, XConstants.PropertyChangeMask);
 248     }
 249 
 250 
 251    /*
 252     * Some users may not need to know about selection changes,
 253     * just owner ship changes, They would specify a zero extra mask.
 254     */
 255 
 256     public XMSelection (String selname, long extraMask) {
 257 
 258         synchronized (this) {
 259             selectionName = selname;
 260             atoms = new XAtom[getNumberOfScreens()];
 261             owners = new long[getNumberOfScreens()];
 262         }
 263         select(extraMask);
 264     }
 265 
 266 
 267 
 268     public synchronized void addSelectionListener(XMSelectionListener listener) {
 269         if (listeners == null) {
 270             listeners = new Vector<>();
 271         }
 272         listeners.add(listener);
 273     }
 274 
 275     public synchronized void removeSelectionListener(XMSelectionListener listener) {
 276         if (listeners != null) {
 277             listeners.remove(listener);
 278         }
 279     }
 280 
 281     synchronized Collection<XMSelectionListener> getListeners() {
 282         return listeners;
 283     }
 284 
 285     synchronized XAtom getAtom(int screen) {
 286         if (atoms != null) {
 287             return atoms[screen];
 288         }
 289         return null;
 290     }
 291 
 292     synchronized void setAtom(XAtom a, int screen) {
 293         if (atoms != null) {
 294             atoms[screen] = a;
 295         }
 296     }
 297 
 298     synchronized long getOwner(int screen) {
 299         if (owners != null) {
 300             return owners[screen];
 301         }
 302         return 0;
 303     }
 304 
 305     synchronized void setOwner(long owner, int screen) {
 306         if (owners != null) {
 307             owners[screen] = owner;
 308         }
 309     }
 310 
 311     synchronized String getName() {
 312         return selectionName;
 313     }
 314 
 315 
 316     synchronized void dispatchSelectionChanged( XPropertyEvent ev, int screen) {
 317         if (log.isLoggable(PlatformLogger.Level.FINE)) {
 318             log.fine("Selection Changed : Screen = " + screen + "Event =" + ev);
 319         }
 320         if (listeners != null) {
 321             Iterator<XMSelectionListener> iter = listeners.iterator();
 322             while (iter.hasNext()) {
 323                 XMSelectionListener disp = iter.next();
 324                 disp.selectionChanged(screen, this, ev.get_window(), ev);
 325             }
 326         }
 327     }
 328 
 329     synchronized void dispatchOwnerDeath(XDestroyWindowEvent de, int screen) {
 330         if (log.isLoggable(PlatformLogger.Level.FINE)) {
 331             log.fine("Owner dead : Screen = " + screen + "Event =" + de);
 332         }
 333         if (listeners != null) {
 334             Iterator<XMSelectionListener> iter = listeners.iterator();
 335             while (iter.hasNext()) {
 336                 XMSelectionListener disp = iter.next();
 337                 disp.ownerDeath(screen, this, de.get_window());
 338 
 339             }
 340         }
 341     }
 342 
 343     void dispatchSelectionEvent(XEvent xev, int screen) {
 344         if (log.isLoggable(PlatformLogger.Level.FINE)) {
 345             log.fine("Event =" + xev);
 346         }
 347         if (xev.get_type() == XConstants.DestroyNotify) {
 348             XDestroyWindowEvent de = xev.get_xdestroywindow();
 349             dispatchOwnerDeath( de, screen);
 350         }
 351         else if (xev.get_type() == XConstants.PropertyNotify)  {
 352             XPropertyEvent xpe = xev.get_xproperty();
 353             dispatchSelectionChanged( xpe, screen);
 354         }
 355     }
 356 
 357 
 358     synchronized void dispatchOwnerChangedEvent(XEvent ev, int screen, long owner, long data, long timestamp) {
 359         if (listeners != null) {
 360             Iterator<XMSelectionListener> iter = listeners.iterator();
 361             while (iter.hasNext()) {
 362                 XMSelectionListener disp = iter.next();
 363                 disp.ownerChanged(screen,this, owner, data, timestamp);
 364             }
 365         }
 366     }
 367 
 368 
 369 }