root/oldXMenu/Activate.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. XMenuActivateSetWaitFunction
  2. XMenuActivateSetTranslateFunction
  3. XMenuActivate

     1 /* Copyright    Massachusetts Institute of Technology    1985   */
     2 
     3 /*
     4 
     5 Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
     6 
     7 Permission to use, copy, modify, and distribute this
     8 software and its documentation for any purpose and without
     9 fee is hereby granted, provided that the above copyright
    10 notice appear in all copies and that both that copyright
    11 notice and this permission notice appear in supporting
    12 documentation, and that the name of M.I.T. not be used in
    13 advertising or publicity pertaining to distribution of the
    14 software without specific, written prior permission.
    15 M.I.T. makes no representations about the suitability of
    16 this software for any purpose.  It is provided "as is"
    17 without express or implied warranty.
    18 
    19 */
    20 
    21 
    22 
    23 /*
    24 Copyright (C) 2001-2023 Free Software Foundation, Inc.
    25 
    26 This program is free software: you can redistribute it and/or modify
    27 it under the terms of the GNU General Public License as published by
    28 the Free Software Foundation, either version 3 of the License, or (at
    29 your option) any later version.
    30 
    31 This program is distributed in the hope that it will be useful,
    32 but WITHOUT ANY WARRANTY; without even the implied warranty of
    33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    34 GNU General Public License for more details.
    35 
    36 You should have received a copy of the GNU General Public License
    37 along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
    38 
    39 /*
    40  * XMenu:       MIT Project Athena, X Window system menu package
    41  *
    42  *      XMenuActivate - Maps a given menu to the display and activates
    43  *                      the menu for user selection.  The user is allowed to
    44  *                      specify which pane and selection will be current,
    45  *                      the X and Y location of the menu (relative to the
    46  *                      parent window) and the mouse button event mask that
    47  *                      will be used to identify a selection request.
    48  *
    49  *                      A menu selection is shown to be current by placing
    50  *                      a highlight box around the selection as the mouse
    51  *                      cursor enters its active region.  Inactive selections
    52  *                      will not be highlighted.  As the mouse cursor moved
    53  *                      from one menu pane to another menu pane the pane being
    54  *                      entered is raised and made current and the pane being
    55  *                      left is lowered.
    56  *
    57  *                      Anytime XMenuActivate returns, the p_num and
    58  *                      s_num are left at their last known values (i.e.,
    59  *                      the last known current pane and selection indices).
    60  *                      The following are the defined return states:
    61  *
    62  *                      1)      If at any time an error occurs the data
    63  *                              pointer is left untouched and XM_FAILURE
    64  *                              is returned.
    65  *
    66  *                      2)      When a selection request is received (i.e.,
    67  *                              when the specified mouse event occurs) the
    68  *                              data pointer will be set to the data
    69  *                              associated with the particular selection
    70  *                              current at the time of the selection request
    71  *                              and XM_SUCCESS is returned.
    72  *
    73  *                      3)      If no selection was current at the time a
    74  *                              selection request is made the data pointer
    75  *                              will be left untouched and XM_NO_SELECT will
    76  *                              be returned.
    77  *
    78  *                      4)      If the selection that was current at the time
    79  *                              a selection request is made is not an active
    80  *                              selection the data pointer will be left
    81  *                              untouched and XM_IA_SELECT will be returned.
    82  *
    83  *                      Since X processes events in an asynchronous manner
    84  *                      it is likely that XMenuActivate will encounter
    85  *                      a "foreign event" while it is executing.  Foreign
    86  *                      events are handled in one of three ways:
    87  *
    88  *                      1)      The event is discarded.  This is the default
    89  *                              mode and requires no action on the part of the
    90  *                              application.
    91  *
    92  *                      2)      The application has identified an asynchronous
    93  *                              event handler that will be called and the
    94  *                              foreign event handed off to it.  Note:
    95  *                              AEQ mode disables this mode temporarily.
    96  *
    97  *                      3)      The application has enabled asynchronous event
    98  *                              queuing mode.  In this mode all foreign events
    99  *                              will be queued up until XMenuActivate
   100  *                              terminates; at which time they will be
   101  *                              returned to the X event queue.  As long as
   102  *                              AEQ mode is enabled any asynchronous event
   103  *                              handler as temporarily disabled.
   104  *
   105  *                      Any events encountered while taking down the menu
   106  *                      (i.e., exposure events from occluded windows) will
   107  *                      automatically be returned to the X event queue after
   108  *                      XMenuActivate has cleaned the queue of any of its own
   109  *                      events that are no longer needed.
   110  *
   111  *      Author:         Tony Della Fera, DEC
   112  *                      March 12, 1986
   113  *
   114  */
   115 
   116 #include "XMenuInt.h"
   117 #include <X11/keysym.h>
   118 
   119 /* For debug, set this to 0 to not grab the keyboard on menu popup */
   120 int x_menu_grab_keyboard = 1;
   121 
   122 static Wait_func wait_func;
   123 static void* wait_data;
   124 static Translate_func translate_func = NULL;
   125 
   126 void
   127 XMenuActivateSetWaitFunction (Wait_func func, void *data)
   128 {
   129   wait_func = func;
   130   wait_data = data;
   131 }
   132 
   133 void
   134 XMenuActivateSetTranslateFunction (Translate_func func)
   135 {
   136   translate_func = func;
   137 }
   138 
   139 int
   140 XMenuActivate(
   141     register Display *display,          /* Display to put menu on. */
   142     register XMenu *menu,               /* Menu to activate. */
   143     int *p_num,                         /* Pane number selected. */
   144     int *s_num,                         /* Selection number selected. */
   145     int x_pos,                          /* X coordinate of menu position. */
   146     int y_pos,                          /* Y coordinate of menu position. */
   147     unsigned int event_mask,            /* Mouse button event mask. */
   148     char **data,                        /* Pointer to return data value. */
   149     void (*help_callback) (char const *, int, int)) /* Help callback.  */
   150 {
   151     int status;                         /* X routine call status. */
   152     int orig_x;                         /* Upper left menu origin X coord. */
   153     int orig_y;                         /* Upper left menu origin Y coord. */
   154     int ret_val;                        /* Return value. */
   155 
   156     register XMPane *p_ptr;             /* Current XMPane. */
   157     register XMPane *event_xmp;         /* Event XMPane pointer. */
   158     register XMPane *cur_p;             /* Current pane. */
   159     register XMSelect *cur_s;           /* Current selection. */
   160     XMWindow *event_xmw;                /* Event XMWindow pointer. */
   161     XEvent event;                       /* X input event. */
   162     XEvent peek_event;                  /* X input peek ahead event. */
   163 
   164     Bool selection = False;             /* Selection has been made. */
   165     Bool forward = True;                /* Moving forward in the pane list. */
   166 
   167     Window root, child;
   168     int root_x, root_y, win_x, win_y;
   169     unsigned int mask;
   170     KeySym keysym;
   171 
   172     /*
   173      * Define and allocate a foreign event queue to hold events
   174      * that don't belong to XMenu.  These events are later restored
   175      * to the X event queue.
   176      */
   177     typedef struct _xmeventque {
   178         XEvent event;
   179         struct _xmeventque *next;
   180     } XMEventQue;
   181 
   182     XMEventQue *feq = NULL;             /* Foreign event queue. */
   183     XMEventQue *feq_tmp;                /* Foreign event queue temporary. */
   184 
   185     /*
   186      * If there are no panes in the menu then return failure
   187      * because the menu is not initialized.
   188      */
   189     if (menu->p_count == 0) {
   190         _XMErrorCode = XME_NOT_INIT;
   191         return(XM_FAILURE);
   192     }
   193 
   194     /*
   195      * Find the desired current pane.
   196      */
   197     cur_p = _XMGetPanePtr(menu, *p_num);
   198     if (cur_p == NULL) {
   199         return(XM_FAILURE);
   200     }
   201     cur_p->activated = cur_p->active;
   202 
   203     /*
   204      * Find the desired current selection.
   205      * If the current selection index is out of range a null current selection
   206      * will be assumed and the cursor will be placed in the current pane
   207      * header.
   208      */
   209     cur_s = _XMGetSelectionPtr(cur_p, *s_num);
   210 
   211     /*
   212      * Compute origin of menu so that cursor is in
   213      * Correct pane and selection.
   214      */
   215     _XMTransToOrigin(display,
   216                      menu,
   217                      cur_p, cur_s,
   218                      x_pos, y_pos,
   219                      &orig_x, &orig_y);
   220     menu->x_pos = orig_x;       /* Store X and Y coords of menu. */
   221     menu->y_pos = orig_y;
   222 
   223     if (XMenuRecompute(display, menu) == XM_FAILURE) {
   224         return(XM_FAILURE);
   225     }
   226 
   227     /*
   228      * Flush the window creation queue.
   229      * This batches all window creates since lazy evaluation
   230      * is more efficient than individual evaluation.
   231      * This routine also does an XFlush().
   232      */
   233     if (_XMWinQueFlush(display, menu, cur_p, cur_s) == _FAILURE) {
   234         return(XM_FAILURE);
   235     }
   236 
   237     /*
   238      * Make sure windows are in correct order (in case we were passed
   239      * an already created menu in incorrect order.)
   240      */
   241     for(p_ptr = menu->p_list->next; p_ptr != cur_p; p_ptr = p_ptr->next)
   242         XRaiseWindow(display, p_ptr->window);
   243     for(p_ptr = menu->p_list->prev; p_ptr != cur_p->prev; p_ptr = p_ptr->prev)
   244         XRaiseWindow(display, p_ptr->window);
   245 
   246     /*
   247      * Make sure all selection windows are mapped.
   248      */
   249     for (
   250         p_ptr = menu->p_list->next;
   251         p_ptr != menu->p_list;
   252         p_ptr = p_ptr->next
   253     ){
   254         XMapSubwindows(display, p_ptr->window);
   255     }
   256 
   257     /*
   258      * Synchronize the X buffers and the event queue.
   259      * From here on, all events in the queue that don't belong to
   260      * XMenu are sent back to the application via an application
   261      * provided event handler or discarded if the application has
   262      * not provided an event handler.
   263      */
   264     XSync(display, 0);
   265 
   266     /*
   267      * Grab the mouse for menu input.
   268      */
   269 
   270     status = XGrabPointer(
   271                           display,
   272                           menu->parent,
   273                           True,
   274                           event_mask,
   275                           GrabModeAsync,
   276                           GrabModeAsync,
   277                           None,
   278                           menu->mouse_cursor,
   279                           CurrentTime
   280                           );
   281     if (status == Success && x_menu_grab_keyboard)
   282       {
   283         status = XGrabKeyboard (display,
   284                                 menu->parent,
   285                                 False,
   286                                 GrabModeAsync,
   287                                 GrabModeAsync,
   288                                 CurrentTime);
   289         if (status != Success)
   290           XUngrabPointer(display, CurrentTime);
   291       }
   292 
   293     if (status == _X_FAILURE) {
   294         _XMErrorCode = XME_GRAB_MOUSE;
   295         return(XM_FAILURE);
   296     }
   297 
   298     /*
   299      * Map the menu panes.
   300      */
   301     XMapWindow(display, cur_p->window);
   302     for (p_ptr = menu->p_list->next;
   303          p_ptr != cur_p;
   304          p_ptr = p_ptr->next)
   305       XMapWindow(display, p_ptr->window);
   306     for (p_ptr = cur_p->next;
   307          p_ptr != menu->p_list;
   308          p_ptr = p_ptr->next)
   309       XMapWindow(display, p_ptr->window);
   310 
   311     XRaiseWindow(display, cur_p->window);       /* Make sure current */
   312                                                 /* pane is on top. */
   313 
   314     cur_s = NULL;                       /* Clear current selection. */
   315 
   316     /*
   317      * Begin event processing loop.
   318      */
   319     while (1) {
   320         if (wait_func) (*wait_func) (wait_data);
   321         XNextEvent(display, &event);    /* Get next event. */
   322         switch (event.type) {           /* Dispatch on the event type. */
   323     case Expose:
   324             event_xmp = (XMPane *)XLookUpAssoc(display,
   325                                                menu->assoc_tab,
   326                                                event.xexpose.window);
   327             if (event_xmp == NULL) {
   328                 /*
   329                  * If AEQ mode is enabled then queue the event.
   330                  */
   331                 if (menu->aeq) {
   332                     feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
   333                     if (feq_tmp == NULL) {
   334                         _XMErrorCode = XME_CALLOC;
   335                         return(XM_FAILURE);
   336                     }
   337                     feq_tmp->event = event;
   338                     feq_tmp->next = feq;
   339                     feq = feq_tmp;
   340                 }
   341                 else if (_XMEventHandler) (*_XMEventHandler)(&event);
   342                 break;
   343             }
   344             if (event_xmp->activated) {
   345                 XSetWindowBackground(display,
   346                                      event_xmp->window,
   347                                      menu->bkgnd_color);
   348             }
   349             else {
   350                 XSetWindowBackgroundPixmap(display,
   351                                            event_xmp->window,
   352                                            menu->inact_pixmap);
   353             }
   354             _XMRefreshPane(display, menu, event_xmp);
   355             break;
   356     case EnterNotify:
   357             /*
   358              * First wait a small period of time, and see
   359              * if another EnterNotify event follows hard on the
   360              * heels of this one. i.e., the user is simply
   361              * "passing through". If so, ignore this one.
   362              */
   363 
   364             event_xmw = (XMWindow *)XLookUpAssoc(display,
   365                                                  menu->assoc_tab,
   366                                                  event.xcrossing.window);
   367             if (event_xmw == NULL) break;
   368             if (event_xmw->type == SELECTION) {
   369                 /*
   370                  * We have entered a selection.
   371                  */
   372                 /* if (XPending(display) == 0) usleep(150000); */
   373                 if (XPending(display) != 0) {
   374                     XPeekEvent(display, &peek_event);
   375                     if(peek_event.type == LeaveNotify) {
   376                         break;
   377                     }
   378                 }
   379                 cur_s = (XMSelect *)event_xmw;
   380                 help_callback (cur_s->help_string,
   381                                cur_p->serial, cur_s->serial);
   382 
   383                 /*
   384                  * If the pane we are in is active and the
   385                  * selection entered is active then activate
   386                  * the selection.
   387                  */
   388                 if (cur_p->active && cur_s->active > 0) {
   389                     cur_s->activated = 1;
   390                     _XMRefreshSelection(display, menu, cur_s);
   391                 }
   392             }
   393             else {
   394                 /*
   395                  * We have entered a pane.
   396                  */
   397                 /* if (XPending(display) == 0) usleep(150000); */
   398                 if (XPending(display) != 0) {
   399                     XPeekEvent(display, &peek_event);
   400                     if (peek_event.type == EnterNotify) break;
   401                 }
   402                 XQueryPointer(display,
   403                               menu->parent,
   404                               &root, &child,
   405                               &root_x, &root_y,
   406                               &win_x, &win_y,
   407                               &mask);
   408                 event_xmp = (XMPane *)XLookUpAssoc(display,
   409                                                    menu->assoc_tab,
   410                                                    child);
   411                 if (event_xmp == NULL) break;
   412                 if (event_xmp == cur_p) break;
   413                 if (event_xmp->serial > cur_p->serial) forward = True;
   414                 else forward = False;
   415                 p_ptr = cur_p;
   416                 while (p_ptr != event_xmp) {
   417                     if (forward) p_ptr = p_ptr->next;
   418                     else p_ptr = p_ptr->prev;
   419                     XRaiseWindow(display, p_ptr->window);
   420                 }
   421                 if (cur_p->activated) {
   422                     cur_p->activated = False;
   423                     XSetWindowBackgroundPixmap(display,
   424                                                cur_p->window,
   425                                                menu->inact_pixmap);
   426                     _XMRefreshPane(display, menu, cur_p);
   427                 }
   428                 if (event_xmp->active) event_xmp->activated = True;
   429 #if 1
   430                 /*
   431                  * i suspect the we don't get an EXPOSE event when backing
   432                  * store is enabled; the menu windows content is probably
   433                  * not drawn in when it should be in that case.
   434                  * in that case, this is probably an ugly fix!
   435                  * i hope someone more familiar with this code would
   436                  * take it from here.  -- caveh@eng.sun.com.
   437                  */
   438                 XSetWindowBackground(display,
   439                                      event_xmp->window,
   440                                      menu->bkgnd_color);
   441                 _XMRefreshPane(display, menu, event_xmp);
   442 #endif
   443                 cur_p = event_xmp;
   444             }
   445             break;
   446     case LeaveNotify:
   447             event_xmw = (XMWindow *)XLookUpAssoc(
   448                                                  display,
   449                                                  menu->assoc_tab,
   450                                                  event.xcrossing.window
   451                                                  );
   452             if (event_xmw == NULL) break;
   453             if(cur_s == NULL) break;
   454 
   455             /*
   456              * If the current selection was activated then
   457              * deactivate it.
   458              */
   459             /* Emacs specific, HELP_STRING cannot be validly NULL
   460              * in the real XMenu library.  */
   461             help_callback (NULL, cur_p->serial, cur_s->serial);
   462             if (cur_s->activated) {
   463                 cur_s->activated = False;
   464                 _XMRefreshSelection(display, menu, cur_s);
   465             }
   466             cur_s = NULL;
   467             break;
   468 
   469     case ButtonPress:
   470     case ButtonRelease:
   471                 *p_num = cur_p->serial;
   472                 /*
   473                  * Check to see if there is a current selection.
   474                  */
   475                 if (cur_s != NULL) {
   476                     /*
   477                      * Set the selection number to the current selection.
   478                      */
   479                     *s_num = cur_s->serial;
   480                     /*
   481                      * If the current selection was activated then
   482                      * we have a valid selection otherwise we have
   483                      * an inactive selection.
   484                      */
   485                     if (cur_s->activated) {
   486                         *data = cur_s->data;
   487                         ret_val = XM_SUCCESS;
   488                     }
   489                     else {
   490                         ret_val = XM_IA_SELECT;
   491                     }
   492                 }
   493                 else {
   494                     /*
   495                      * No selection was current.
   496                      */
   497                     ret_val = XM_NO_SELECT;
   498                 }
   499                 selection = True;
   500                 break;
   501         case KeyPress:
   502         case KeyRelease:
   503                 keysym = XLookupKeysym (&event.xkey, 0);
   504 
   505                 /* Pop down on C-g and Escape.  */
   506                 if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
   507                     || keysym == XK_Escape) /* Any escape, ignore modifiers.  */
   508                   {
   509                     ret_val = XM_NO_SELECT;
   510                     selection = True;
   511                   }
   512                break;
   513             default:
   514                 /*
   515                  * If AEQ mode is enabled then queue the event.
   516                  */
   517                 if (menu->aeq) {
   518                     feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
   519                     if (feq_tmp == NULL) {
   520                         _XMErrorCode = XME_CALLOC;
   521                         return(XM_FAILURE);
   522                     }
   523                     feq_tmp->event = event;
   524                     feq_tmp->next = feq;
   525                     feq = feq_tmp;
   526                 }
   527                 else if (_XMEventHandler) (*_XMEventHandler)(&event);
   528                 break;
   529 #ifdef HAVE_XINPUT2
   530         case GenericEvent:
   531             if (translate_func)
   532               translate_func (&event);
   533 #endif
   534         }
   535         /*
   536          * If a selection has been made, break out of the event loop.
   537          */
   538         if (selection == True) break;
   539     }
   540 
   541     /*
   542      * Unmap the menu.
   543      */
   544     for ( p_ptr = menu->p_list->next;
   545          p_ptr != menu->p_list;
   546          p_ptr = p_ptr->next)
   547       {
   548           XUnmapWindow(display, p_ptr->window);
   549       }
   550 
   551     /*
   552      * Ungrab the mouse.
   553      */
   554     XUngrabPointer(display, CurrentTime);
   555     XUngrabKeyboard(display, CurrentTime);
   556 
   557     /*
   558      * Restore bits under where the menu was if we managed
   559      * to save them and free the pixmap.
   560      */
   561 
   562     /*
   563      * If there is a current selection deactivate it.
   564      */
   565     if (cur_s != NULL) cur_s->activated = 0;
   566 
   567     /*
   568      * Deactivate the current pane.
   569      */
   570     cur_p->activated = 0;
   571     XSetWindowBackgroundPixmap(display, cur_p->window, menu->inact_pixmap);
   572 
   573     /*
   574      * Synchronize the X buffers and the X event queue.
   575      */
   576     XSync(display, 0);
   577 
   578     /*
   579      * Dispatch any events remaining on the queue.
   580      */
   581     while (QLength(display)) {
   582         /*
   583          * Fetch the next event.
   584          */
   585         XNextEvent(display, &event);
   586 
   587         /*
   588          * Discard any events left on the queue that belong to XMenu.
   589          * All others are held and then returned to the event queue.
   590          */
   591         switch (event.type) {
   592             case Expose:
   593             case EnterNotify:
   594             case LeaveNotify:
   595             case ButtonPress:
   596             case ButtonRelease:
   597                 /*
   598                  * Does this event belong to one of XMenu's windows?
   599                  * If so, discard it and process the next event.
   600                  * If not fall through and treat it as a foreign event.
   601                  */
   602                 event_xmp = (XMPane *)XLookUpAssoc(
   603                                                    display,
   604                                                    menu->assoc_tab,
   605                                                    event.xbutton.window
   606                                                    );
   607                 if (event_xmp != NULL) continue;
   608                 FALLTHROUGH;
   609             default:
   610                 /*
   611                  * This is a foreign event.
   612                  * Queue it for later return to the X event queue.
   613                  */
   614                 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
   615                 if (feq_tmp == NULL) {
   616                     _XMErrorCode = XME_CALLOC;
   617                     return(XM_FAILURE);
   618                 }
   619                 feq_tmp->event = event;
   620                 feq_tmp->next = feq;
   621                 feq = feq_tmp;
   622             }
   623     }
   624     /*
   625      * Return any foreign events that were queued to the X event queue.
   626      */
   627     while (feq != NULL) {
   628         feq_tmp = feq;
   629         XPutBackEvent(display, &feq_tmp->event);
   630         feq = feq_tmp->next;
   631         free((char *)feq_tmp);
   632     }
   633 
   634     wait_func = 0;
   635 
   636     /*
   637      * Return successfully.
   638      */
   639     _XMErrorCode = XME_NO_ERROR;
   640     return(ret_val);
   641 
   642 }

/* [<][>][^][v][top][bottom][index][help] */