jdk/src/solaris/native/sun/awt/awt_mgrsel.c
changeset 11319 a3d37054381f
parent 11318 e114b7d53b9b
parent 11239 885050364691
child 11320 1ed269f7cc73
equal deleted inserted replaced
11318:e114b7d53b9b 11319:a3d37054381f
     1 /*
       
     2  * Copyright (c) 2003, 2004, 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 #ifdef HEADLESS
       
    27     #error This file should not be included in headless library
       
    28 #endif
       
    29 
       
    30 #include "awt_mgrsel.h"
       
    31 
       
    32 static Atom XA_MANAGER = None;
       
    33 
       
    34 /*
       
    35  * Structures that describes the manager selection AWT listens to with
       
    36  * callabacks to the subsytems interested in the selection.  (We only
       
    37  * listen to a couple of selections, so linear search is enough).
       
    38  */
       
    39 struct AwtMgrsel {
       
    40     char *selname;              /* base name of selection atoms */
       
    41     Atom *per_scr_atoms;        /* per-screen selection atoms (ICCCM 1.2.6) */
       
    42     Atom *per_scr_owners;       /* windows currently owning the selection */
       
    43     long extra_mask;            /* extra events to listen to on owners */
       
    44     void *cookie;
       
    45     void (*callback_event)(int, XEvent *, void *); /* extra_mask events */
       
    46     void (*callback_owner)(int, Window, long *, void *); /* owner changes */
       
    47     struct AwtMgrsel *next;
       
    48 };
       
    49 
       
    50 static struct AwtMgrsel *mgrsel_list = NULL;
       
    51 
       
    52 
       
    53 static int awt_mgrsel_screen(Window w);
       
    54 static Window awt_mgrsel_select_per_screen(Atom, long);
       
    55 static int awt_mgrsel_managed(XClientMessageEvent *mgrown);
       
    56 static int awt_mgrsel_unmanaged(XDestroyWindowEvent *ev);
       
    57 
       
    58 #ifdef DEBUG
       
    59 static void awt_mgrsel_dtraceManaged(XClientMessageEvent *mgrown);
       
    60 #endif
       
    61 
       
    62 
       
    63 
       
    64 /*
       
    65  * Find which screen the window W is the root of.
       
    66  * Returns the screen number, or -1 if W is not a root.
       
    67  */
       
    68 static int
       
    69 awt_mgrsel_screen(Window w)
       
    70 {
       
    71     Display *dpy = awt_display;
       
    72     int scr;
       
    73 
       
    74     for (scr = 0; scr < ScreenCount(dpy); ++scr) {
       
    75         if (w == RootWindow(dpy, scr)) {
       
    76             return (scr);
       
    77         }
       
    78     }
       
    79 
       
    80     return (-1);
       
    81 }
       
    82 
       
    83 
       
    84 /************************************************************************
       
    85  * For every one that asketh receiveth; and he that seeketh findeth;
       
    86  * and to him that knocketh it shall be opened.  (Luke 11:10).
       
    87  */
       
    88 
       
    89 
       
    90 /*
       
    91  * A method for a subsytem to express its interest in a certain
       
    92  * manager selection.
       
    93  *
       
    94  * If owner changes, the callback_owner will be called with the screen
       
    95  * number and the new owning window when onwership is established, or
       
    96  * None if the owner is gone.
       
    97  *
       
    98  * Events in extra_mask are selected for on owning windows (exsiting
       
    99  * ones and on new owners when established) and callback_event will be
       
   100  * called with the screen number and an event.
       
   101  *
       
   102  * The function returns an array of current owners.  The size of the
       
   103  * array is ScreenCount(awt_display).  The array is "owned" by this
       
   104  * module and should be considered by the caller as read-only.
       
   105  */
       
   106 const Window *
       
   107 awt_mgrsel_select(const char *selname, long extra_mask,
       
   108                   void *cookie,
       
   109                   void (*callback_event)(int, XEvent *, void *),
       
   110                   void (*callback_owner)(int, Window, long *, void *))
       
   111 {
       
   112     Display *dpy = awt_display;
       
   113     struct AwtMgrsel *mgrsel;
       
   114     Atom *per_scr_atoms;
       
   115     Window *per_scr_owners;
       
   116     char *namesbuf;
       
   117     char **names;
       
   118     int per_scr_sz;
       
   119     int nscreens = ScreenCount(dpy);
       
   120     int scr;
       
   121     Status status;
       
   122 
       
   123     DASSERT(selname != NULL);
       
   124     DTRACE_PRINTLN1("MG: select: %s", selname);
       
   125 
       
   126     /* buffer size for one per-screen atom name */
       
   127     per_scr_sz = strlen(selname) + /* "_S" */ 2 + /* %2d */ + 2 /* '\0' */+ 1;
       
   128 
       
   129     namesbuf = malloc(per_scr_sz * nscreens);  /* actual storage for names */
       
   130     names = malloc(sizeof(char *) * nscreens); /* pointers to names */
       
   131     per_scr_atoms = malloc(sizeof(Atom) * nscreens);
       
   132     per_scr_owners = malloc(sizeof(Window) * nscreens);
       
   133     mgrsel = malloc(sizeof(struct AwtMgrsel));
       
   134 
       
   135     if (namesbuf == NULL || names == NULL || per_scr_atoms == NULL
       
   136         || per_scr_owners == NULL || mgrsel == NULL)
       
   137     {
       
   138         DTRACE_PRINTLN("MG: select: unable to allocate memory");
       
   139         if (namesbuf != NULL) free(namesbuf);
       
   140         if (names != NULL) free(names);
       
   141         if (per_scr_atoms != NULL) free(per_scr_atoms);
       
   142         if (per_scr_owners != NULL) free(per_scr_owners);
       
   143         if (mgrsel != NULL) free(mgrsel);
       
   144         return (NULL);
       
   145     }
       
   146 
       
   147 
       
   148     for (scr = 0; scr < nscreens; ++scr) {
       
   149         size_t sz;
       
   150 
       
   151         names[scr] = &namesbuf[per_scr_sz * scr];
       
   152         sz = snprintf(names[scr], per_scr_sz, "%s_S%-d", selname, scr);
       
   153         DASSERT(sz < per_scr_sz);
       
   154     }
       
   155 
       
   156     status = XInternAtoms(dpy, names, nscreens, False, per_scr_atoms);
       
   157 
       
   158     free(names);
       
   159     free(namesbuf);
       
   160 
       
   161     if (status == 0) {
       
   162         DTRACE_PRINTLN("MG: select: XInternAtoms failed");
       
   163         free(per_scr_atoms);
       
   164         free(per_scr_owners);
       
   165         return (NULL);
       
   166     }
       
   167 
       
   168     mgrsel->selname = strdup(selname);
       
   169     mgrsel->per_scr_atoms = per_scr_atoms;
       
   170     mgrsel->per_scr_owners = per_scr_owners;
       
   171     mgrsel->extra_mask = extra_mask;
       
   172     mgrsel->cookie = cookie;
       
   173     mgrsel->callback_event = callback_event;
       
   174     mgrsel->callback_owner = callback_owner;
       
   175 
       
   176     for (scr = 0; scr < nscreens; ++scr) {
       
   177         Window owner;
       
   178 
       
   179         owner = awt_mgrsel_select_per_screen(per_scr_atoms[scr], extra_mask);
       
   180         mgrsel->per_scr_owners[scr] = owner;
       
   181 #ifdef DEBUG
       
   182         if (owner == None) {
       
   183             DTRACE_PRINTLN1("MG:   screen %d - None", scr);
       
   184         } else {
       
   185             DTRACE_PRINTLN2("MG:   screen %d - 0x%08lx", scr, owner);
       
   186         }
       
   187 #endif
       
   188     }
       
   189 
       
   190     mgrsel->next = mgrsel_list;
       
   191     mgrsel_list = mgrsel;
       
   192 
       
   193     return (per_scr_owners);
       
   194 }
       
   195 
       
   196 
       
   197 static Window
       
   198 awt_mgrsel_select_per_screen(Atom selection, long extra_mask)
       
   199 {
       
   200     Display *dpy = awt_display;
       
   201     Window owner;
       
   202 
       
   203     XGrabServer(dpy);
       
   204 
       
   205     owner = XGetSelectionOwner(dpy, selection);
       
   206     if (owner == None) {
       
   207         /* we'll get notified later if one arrives */
       
   208         XUngrabServer(dpy);
       
   209         /* Workaround for bug 5039226 */
       
   210         XSync(dpy, False);
       
   211         return (None);
       
   212     }
       
   213 
       
   214     /*
       
   215      * Select for StructureNotifyMask to get DestroyNotify when owner
       
   216      * is gone.  Also select for any additional events caller is
       
   217      * interested in (e.g. PropertyChangeMask).  Caller will be
       
   218      * notifed of these events via ... XXX ...
       
   219      */
       
   220     XSelectInput(dpy, owner, StructureNotifyMask | extra_mask);
       
   221 
       
   222     XUngrabServer(dpy);
       
   223     /* Workaround for bug 5039226 */
       
   224     XSync(dpy, False);
       
   225     return (owner);
       
   226 }
       
   227 
       
   228 
       
   229 /************************************************************************
       
   230  * And so I saw the wicked buried, who had come and gone from the
       
   231  * place of the holy, and they were forgotten in the city where they
       
   232  * had so done: this is also vanity.  (Eccl 8:10)
       
   233  */
       
   234 
       
   235 #ifdef DEBUG
       
   236 /*
       
   237  * Print the message from the new manager that announces it acquired
       
   238  * ownership.
       
   239  */
       
   240 static void
       
   241 awt_mgrsel_dtraceManaged(XClientMessageEvent *mgrown)
       
   242 {
       
   243     Display *dpy = awt_display;
       
   244     Atom selection;
       
   245     char *selname, *print_selname;
       
   246     int scr;
       
   247 
       
   248     scr = awt_mgrsel_screen(mgrown->window);
       
   249 
       
   250     selection = mgrown->data.l[1];
       
   251     print_selname = selname = XGetAtomName(dpy, selection);
       
   252     if (selname == NULL) {
       
   253         if (selection == None) {
       
   254             print_selname = "<None>";
       
   255         } else {
       
   256             print_selname = "<Unknown>";
       
   257         }
       
   258     }
       
   259 
       
   260     DTRACE_PRINTLN4("MG: new MANAGER for %s: screen %d, owner 0x%08lx (@%lu)",
       
   261                    print_selname, scr,
       
   262                    mgrown->data.l[2],  /* the window owning the selection */
       
   263                    mgrown->data.l[0]); /* timestamp */
       
   264     DTRACE_PRINTLN4("MG:   %ld %ld / 0x%lx 0x%lx", /* extra data */
       
   265                     mgrown->data.l[3], mgrown->data.l[4],
       
   266                     mgrown->data.l[3], mgrown->data.l[4]);
       
   267 
       
   268     if (selname != NULL) {
       
   269         XFree(selname);
       
   270     }
       
   271 }
       
   272 #endif /* DEBUG */
       
   273 
       
   274 
       
   275 static int
       
   276 awt_mgrsel_managed(XClientMessageEvent *mgrown)
       
   277 {
       
   278     Display *dpy = awt_display;
       
   279     struct AwtMgrsel *mgrsel;
       
   280     int scr;
       
   281 
       
   282     long timestamp;
       
   283     Atom selection;
       
   284     Window owner;
       
   285     long *data;
       
   286 
       
   287     if (mgrown->message_type != XA_MANAGER) {
       
   288         DTRACE_PRINTLN("MG: ClientMessage type != MANAGER, ignoring");
       
   289         return (0);
       
   290     }
       
   291 
       
   292     scr = awt_mgrsel_screen(mgrown->window);
       
   293 
       
   294 #ifdef DEBUG
       
   295     awt_mgrsel_dtraceManaged(mgrown);
       
   296 #endif
       
   297 
       
   298     if (scr < 0) {
       
   299         DTRACE_PRINTLN("MG: MANAGER ClientMessage with a non-root window!");
       
   300         return (0);
       
   301     }
       
   302 
       
   303     timestamp = mgrown->data.l[0];
       
   304     selection = mgrown->data.l[1];
       
   305     owner     = mgrown->data.l[2];
       
   306     data      = &mgrown->data.l[3]; /* long[2], selection specific */
       
   307 
       
   308     /* is this a selection we are intrested in? */
       
   309     for (mgrsel = mgrsel_list; mgrsel != NULL; mgrsel = mgrsel->next) {
       
   310         if (selection == mgrsel->per_scr_atoms[scr])
       
   311             break;
       
   312     }
       
   313 
       
   314     if (mgrsel == NULL) {
       
   315         DTRACE_PRINTLN("MG: not interested in this selection, ignoring");
       
   316         return (0);
       
   317     }
       
   318 
       
   319 
       
   320     mgrsel->per_scr_owners[scr] = owner;
       
   321 
       
   322     XSelectInput(dpy, owner, StructureNotifyMask | mgrsel->extra_mask);
       
   323 
       
   324     /* notify the listener */
       
   325     if (mgrsel->callback_owner != NULL) {
       
   326         (*mgrsel->callback_owner)(scr, owner, data, mgrsel->cookie);
       
   327     }
       
   328 
       
   329     return (1);
       
   330 }
       
   331 
       
   332 
       
   333 static int
       
   334 awt_mgrsel_unmanaged(XDestroyWindowEvent *ev)
       
   335 {
       
   336     Display *dpy = awt_display;
       
   337     struct AwtMgrsel *mgrsel;
       
   338     Window exowner;
       
   339     int scr;
       
   340 
       
   341     exowner = ev->window;       /* selection owner that's gone */
       
   342 
       
   343     /* is this a selection we are intrested in? */
       
   344     for (mgrsel = mgrsel_list; mgrsel != NULL; mgrsel = mgrsel->next) {
       
   345         for (scr = 0; scr < ScreenCount(dpy); ++scr) {
       
   346             if (exowner == mgrsel->per_scr_owners[scr]) {
       
   347                 /* can one window own selections for more than one screen? */
       
   348                 goto out;       /* XXX??? */
       
   349             }
       
   350         }
       
   351     }
       
   352   out:
       
   353     if (mgrsel == NULL) {
       
   354         DTRACE_PRINTLN1("MG: DestroyNotify for 0x%08lx ignored", exowner);
       
   355         return (0);
       
   356     }
       
   357 
       
   358     DTRACE_PRINTLN3("MG: DestroyNotify for 0x%08lx, owner of %s at screen %d",
       
   359                     exowner, mgrsel->selname, scr);
       
   360 
       
   361     /* notify the listener (pass exowner as data???) */
       
   362     if (mgrsel->callback_owner != NULL) {
       
   363         (*mgrsel->callback_owner)(scr, None, NULL, mgrsel->cookie);
       
   364     }
       
   365 
       
   366     return (1);
       
   367 }
       
   368 
       
   369 
       
   370 /*
       
   371  * Hook to be called from toolkit event loop.
       
   372  */
       
   373 int
       
   374 awt_mgrsel_processEvent(XEvent *ev)
       
   375 {
       
   376     Display *dpy = awt_display;
       
   377     struct AwtMgrsel *mgrsel;
       
   378     int scr;
       
   379 
       
   380     if (ev->type == ClientMessage) { /* new manager announces ownership? */
       
   381         if (awt_mgrsel_managed(&ev->xclient))
       
   382             return (1);
       
   383     }
       
   384 
       
   385     if (ev->type == DestroyNotify) { /* manager gives up selection? */
       
   386         if (awt_mgrsel_unmanaged(&ev->xdestroywindow))
       
   387             return (1);
       
   388     }
       
   389 
       
   390     /* is this an event selected on one of selection owners? */
       
   391     for (mgrsel = mgrsel_list; mgrsel != NULL; mgrsel = mgrsel->next) {
       
   392         for (scr = 0; scr < ScreenCount(dpy); ++scr) {
       
   393             if (ev->xany.window == mgrsel->per_scr_owners[scr]) {
       
   394                 /* can one window own selections for more than one screen? */
       
   395                 goto out;       /* XXX??? */
       
   396             }
       
   397         }
       
   398     }
       
   399   out:
       
   400     DTRACE_PRINT2("MG: screen %d, event %d ...  ",
       
   401                   scr, ev->xany.type);
       
   402     if (mgrsel == NULL) {
       
   403         DTRACE_PRINTLN("ignored");
       
   404         return (0);             /* not interested */
       
   405     }
       
   406 
       
   407     DTRACE_PRINT1("%s ...  ", mgrsel->selname);
       
   408     if (mgrsel->callback_event != NULL) {
       
   409         DTRACE_PRINTLN("dispatching");
       
   410         (*mgrsel->callback_event)(scr, ev, mgrsel->cookie);
       
   411     }
       
   412 #ifdef DEBUG
       
   413     else {
       
   414         DTRACE_PRINTLN("no callback");
       
   415     }
       
   416 #endif
       
   417 
       
   418     return (1);
       
   419 }
       
   420 
       
   421 
       
   422 void
       
   423 awt_mgrsel_init(void)
       
   424 {
       
   425     static Boolean inited = False;
       
   426 
       
   427     Display *dpy = awt_display;
       
   428     int scr;
       
   429 
       
   430     if (inited) {
       
   431         return;
       
   432     }
       
   433 
       
   434     XA_MANAGER = XInternAtom(dpy, "MANAGER", False);
       
   435     DASSERT(XA_MANAGER != None);
       
   436 
       
   437 
       
   438     /*
       
   439      * Listen for ClientMessage's on each screen's root.  We hook into
       
   440      * the message loop in the toolkit (with awt_mgrsel_processEvent)
       
   441      * to get the events processed.  We need this for notifications of
       
   442      * new manager acquiring ownership of the manager selection.
       
   443      */
       
   444     for (scr = 0; scr < ScreenCount(dpy); ++scr) {
       
   445         XSelectInput(dpy, RootWindow(dpy, scr), StructureNotifyMask);
       
   446     }
       
   447 
       
   448     inited = True;
       
   449 }