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 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 selectionMap;
79 
80     static {
81         long display = XToolkit.getDisplay();
XToolkit.awtLock()82         XToolkit.awtLock();
83         try {
84             numScreens = XlibWrapper.ScreenCount(display);
85         } finally {
XToolkit.awtUnlock()86             XToolkit.awtUnlock();
87         }
88         XA_MANAGER = XAtom.get("MANAGER");
89         for (int screen = 0; screen < numScreens ; screen ++) {
initScreen(display,screen)90             initScreen(display,screen);
91         }
92 
93         selectionMap = new HashMap();
94     }
95 
initScreen(long display, final int screen)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 
getNumberOfScreens()122     public int getNumberOfScreens() {
123         return numScreens;
124     }
125 
select(long extra_mask)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 
resetOwner(long owner, final int screen)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 
selectPerScreen(final int screen, long extra_mask)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 
processClientMessage(XEvent xev, int screen)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 
processRootEvent(XEvent xev, int screen)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 
getInstance(long selection)237     static XMSelection getInstance(long selection) {
238         return (XMSelection) selectionMap.get(Long.valueOf(selection));
239     }
240 
241 
242     /*
243      * Default constructor specifies PropertyChangeMask as well
244      */
245 
XMSelection(String selname)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 
XMSelection(String selname, long extraMask)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 
addSelectionListener(XMSelectionListener listener)268     public synchronized void addSelectionListener(XMSelectionListener listener) {
269         if (listeners == null) {
270             listeners = new Vector();
271         }
272         listeners.add(listener);
273     }
274 
removeSelectionListener(XMSelectionListener listener)275     public synchronized void removeSelectionListener(XMSelectionListener listener) {
276         if (listeners != null) {
277             listeners.remove(listener);
278         }
279     }
280 
getListeners()281     synchronized Collection getListeners() {
282         return listeners;
283     }
284 
getAtom(int screen)285     synchronized XAtom getAtom(int screen) {
286         if (atoms != null) {
287             return atoms[screen];
288         }
289         return null;
290     }
291 
setAtom(XAtom a, int screen)292     synchronized void setAtom(XAtom a, int screen) {
293         if (atoms != null) {
294             atoms[screen] = a;
295         }
296     }
297 
getOwner(int screen)298     synchronized long getOwner(int screen) {
299         if (owners != null) {
300             return owners[screen];
301         }
302         return 0;
303     }
304 
setOwner(long owner, int screen)305     synchronized void setOwner(long owner, int screen) {
306         if (owners != null) {
307             owners[screen] = owner;
308         }
309     }
310 
getName()311     synchronized String getName() {
312         return selectionName;
313     }
314 
315 
dispatchSelectionChanged( XPropertyEvent ev, int screen)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 iter = listeners.iterator();
322             while (iter.hasNext()) {
323                 XMSelectionListener disp = (XMSelectionListener) iter.next();
324                 disp.selectionChanged(screen, this, ev.get_window(), ev);
325             }
326         }
327     }
328 
dispatchOwnerDeath(XDestroyWindowEvent de, int screen)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 iter = listeners.iterator();
335             while (iter.hasNext()) {
336                 XMSelectionListener disp = (XMSelectionListener) iter.next();
337                 disp.ownerDeath(screen, this, de.get_window());
338 
339             }
340         }
341     }
342 
dispatchSelectionEvent(XEvent xev, int screen)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 
dispatchOwnerChangedEvent(XEvent ev, int screen, long owner, long data, long timestamp)358     synchronized void dispatchOwnerChangedEvent(XEvent ev, int screen, long owner, long data, long timestamp) {
359         if (listeners != null) {
360             Iterator iter = listeners.iterator();
361             while (iter.hasNext()) {
362                 XMSelectionListener disp = (XMSelectionListener) iter.next();
363                 disp.ownerChanged(screen,this, owner, data, timestamp);
364             }
365         }
366     }
367 
368 
369 }
370