toma@tc.fluke.COM (Tom Anderson) (04/07/88)
I recently installed X11.2 at our site, where we are running a mix of Sun-2's and Sun-3's. As with the previous release, most of the stuff works. However, an experimental program that worked with X11.1 no longer works properly - it seems that window entry and exit events have changed somehow... I've included the program below, which is a simple demo of how window entry and exit events can be used to implement a popup menu facility (by mapping each menu selection to a window). In X11.1, the entry and exit events were delivered; in X11.2, they never are. If anyone running X on Suns can try this out and let me know if it works at their site, I would appreciate hearing about the results. Better yet, if you find out why it no longer works, please let me know. If there is some bug in the latest release which causes this behavior, I would also appreciate finding out how to fix it. BTW, the code is contributed to the public domain and can be used for any purpose. As far as I know, the popup menu's look and feel does not conflict with any known proprietary system :-). Thanks, Tom Anderson John Fluke Mfg. (206) 356-5895 toma@fluke p.s. - While I have your attention, does anyone know when Sun is going to get around to improving the performance of the X server for Suns? I know they have a bad case of NIH (Not-Invented-Here), and I also know they are planning to support X via NeWS, but how hard can it be to support a real X server? The one that comes with the MIT distribution clearly needs a lot of work for performance tuning, and the NeWS implementation may not be much better. # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by spock!toma on Wed Apr 6 11:44:47 PDT 1988 # Contents: Makefile mask.cursor popup.c popup.cursor echo x - Makefile sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//' CFLAGS=-g XLIBS=-lX11 popup : popup.c cc $(CFLAGS) -o popup popup.c $(XLIBS) @//E*O*F Makefile// chmod u=rw,g=r,o=r Makefile echo x - mask.cursor sed 's/^@//' > "mask.cursor" <<'@//E*O*F mask.cursor//' #define mask_width 16 #define mask_height 16 static char mask_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0x07, 0xff, 0x0f, 0xff, 0x07, 0xff, 0x07, 0xff, 0x03, 0xff, 0x01, 0xff, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00}; @//E*O*F mask.cursor// chmod u=rw,g=r,o=r mask.cursor echo x - popup.c sed 's/^@//' > "popup.c" <<'@//E*O*F popup.c//' /* * experimental program to interactively create an X window, then pop up menu(s) in the window. */ #include <stdio.h> #include <X11/X.h> #include <X11/Xlib.h> extern char * malloc(); /* * include menu cursor bitmap data */ #include "popup.cursor" #include "mask.cursor" /* * menu button type */ struct menuButton { struct menuButton * next; /* next button in the list */ Window window; /* the button window */ int x, y; /* origin relative to the menu frame */ void (* proc)(); /* button selection procedure */ char label[1]; /* button text: variable-length array */ } MenuButton; #define MENU_BUTTON struct menuButton /* * pop-up menu type */ typedef struct { Window window; /* the menu frame window */ MENU_BUTTON * buttonList; /* list of menu buttons */ MENU_BUTTON ** newButton; /* insertion point for new buttons */ Bool isFrameActive; /* is the cursor currently in the menu frame? */ Bool isButtonActive; /* is the cursor currently in a button? */ Window activeButtonWindow; /* the window number of the active button */ int activeButtonNumber; /* the button number of the active button */ int width, height; /* frame geometry */ int buttonWidth, buttonHeight; /* button geometry */ XFontStruct * font; /* button font info */ GC fontGC; /* the graphics context for drawing button text */ Cursor cursor; /* the menu cursor */ } PopupMenu; /* * create a pop-up menu */ PopupMenu * CreatePopupMenu(display, progName) Display * display; /* the display */ char * progName; /* the program name */ { PopupMenu * pmp; char * fontName; /* the button font name */ if ((pmp = (PopupMenu *) malloc((PopupMenu *) sizeof(PopupMenu))) != (PopupMenu *) 0) { pmp->buttonList = (MENU_BUTTON *) 0; pmp->newButton = &pmp->buttonList; pmp->isButtonActive = pmp->isFrameActive = False; /* find and map the font name */ if ((fontName = XGetDefault(display, progName, "buttonFont")) == (char *) 0) { fontName = "9x15"; } if((pmp->font = XLoadQueryFont(display, fontName)) == (XFontStruct *) 0) { free((char *) pmp); fprintf(stderr, "%s: can't load font %s\n", progName, fontName); return((PopupMenu *) 0); } /* size the button height to fit the button font */ pmp->buttonHeight = pmp->font->max_bounds.ascent + pmp->font->max_bounds.descent + 1; pmp->buttonWidth = pmp->width = pmp->height = 0; } return(pmp); } /* * add a button to a pop-up menu */ void AddMenuItem(menu, label, proc) PopupMenu * menu; /* the pop-up menu */ char * label; /* the button label */ void (* proc)(); /* the button procedure (NOT IMPLEMENTED) */ { register MENU_BUTTON * nmbp; /* menu button ptr. */ int width; /* overall button box width */ /* * allocate the MenuButton struct for this button */ if ((nmbp = (MENU_BUTTON *) malloc(sizeof(MENU_BUTTON) + strlen(label))) != (MENU_BUTTON *) 0) { /* compute the menu button parameters */ nmbp->next = (MENU_BUTTON *) 0; nmbp->proc = proc; nmbp->x = 0; nmbp->y = menu->height; strcpy(nmbp->label, label); /* compute the new menu frame parameters */ menu->height += menu->buttonHeight; width = XTextWidth(menu->font, label, strlen(label)); menu->width = menu->buttonWidth = menu->buttonWidth > width ? menu->buttonWidth : width + 2 * menu->font->max_bounds.width; /* link the new button into the button list */ *(menu->newButton) = nmbp; menu->newButton = &nmbp->next; } } /* * NOTE: should return status */ void InstallPopupMenu(display, screen, menu) Display * display; /* the display */ int screen; /* the screen */ PopupMenu * menu; /* the menu */ { register MENU_BUTTON * mbp; /* the menu button descriptor */ XSetWindowAttributes win_attrs; /* the window's attributes */ XWindowAttributes ret_win_attrs; /* returned window attributes */ XGCValues context_values; /* context loading values */ Pixmap cursorPixmap; /* the cursor pixmap */ Pixmap maskPixmap; /* the cursor mask pixmap */ XColor black, white; /* ? not sure this is the best way to do this */ /* * create the pop-up menu frame window */ win_attrs.background_pixel = WhitePixel(display, screen); win_attrs.border_pixmap = None; win_attrs.border_pixel = BlackPixel(display, screen); /* black border */ win_attrs.backing_store = Always; win_attrs.save_under = True; win_attrs.override_redirect = True; win_attrs.event_mask = ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask; menu->width += 2; /* allow for button border widths */ menu->height += 2; menu->window = XCreateWindow(display, /* parent: */ RootWindow(display, screen), /* x, y, width, height, border_width, depth, */ 0, 0, menu->width, menu->height, 0, 1, /* class, visual, valuemask, attributes */ InputOutput, DefaultVisual(display, screen), CWEventMask | CWBackPixel | CWBorderPixmap | CWSaveUnder | CWBackingStore | CWBorderPixel | CWOverrideRedirect, &win_attrs); /* * read the popup cursor bitmap and set up the cursor */ cursorPixmap = XCreateBitmapFromData(display, menu->window, popup_bits, popup_width, popup_height); maskPixmap = XCreateBitmapFromData(display, menu->window, mask_bits, mask_width, mask_height); if (cursorPixmap != NULL && maskPixmap != NULL) { /* * find the XColors of black and white (there has to a more straightforward * way to do this) */ XGetWindowAttributes(display, menu->window, &ret_win_attrs); black.pixel = BlackPixel(display, screen); XQueryColor(display, ret_win_attrs.colormap, &black); white.pixel = WhitePixel(display, screen); XQueryColor(display, ret_win_attrs.colormap, &white); menu->cursor = XCreatePixmapCursor(display, cursorPixmap, maskPixmap, &black, &white, popup_x_hot, popup_y_hot); XDefineCursor(display, menu->window, menu->cursor); } /* * establish the graphics context used to draw button box text */ context_values.function = GXset; context_values.foreground = BlackPixel(display,screen); context_values.font = menu->font->fid; menu->fontGC = XCreateGC(display, menu->window, GCForeground | GCFunction | GCFont, &context_values); /* * create and map the menu button windows * * NOTE: The cursor mask is included; however, this shouldn't be * necessary, since it should be inherited from the menu frame window. * Something seems amiss in the way the server is handling this. */ win_attrs.event_mask = EnterWindowMask | LeaveWindowMask; win_attrs.cursor = menu->cursor; for (mbp = menu->buttonList ; mbp != (MENU_BUTTON *) 0 ; mbp = mbp->next) { mbp->window = XCreateWindow(display, /* parent: */ menu->window, /* x, y, width, height, border_width, depth, */ mbp->x, mbp->y, menu->buttonWidth, menu->buttonHeight, 1, 1, /* class, visual, valuemask, attributes */ InputOutput, DefaultVisual(display, screen), CWEventMask | CWBackPixel | CWBorderPixmap | CWBorderPixel | CWBackingStore | CWCursor, &win_attrs); XMapWindow(display, mbp->window); /* * draw the button labels' text */ /* XDrawString(display, mbp->window, menu->fontGC, menu->font->max_bounds.width/2, menu->font->max_bounds.ascent, mbp->label, strlen(mbp->label)); */ } } DoPopupMenu(display, screen, menu, x, y) Display * display; /* display */ int screen; /* screen */ PopupMenu * menu; /* menu */ int x, y; /* midpoint of the top bar of the menu */ { register MENU_BUTTON * mbp; /* menu button pointer */ XWindowChanges win_chngs; /* window configuration arg struct */ XWindowAttributes ret_win_attrs; /* returned window attributes */ GC context; /* the graphics context (for drawing lines) */ XGCValues context_values; /* context loading values */ int activeButtonNumber = 0; /* the currently active button */ register int i; /* * locate and pop up the menu window */ XGetWindowAttributes(display, menu->window, &ret_win_attrs); win_chngs.x = x - ret_win_attrs.width / 2; win_chngs.y = y; XConfigureWindow(display, menu->window, CWX | CWY, &win_chngs); XMapRaised(display, menu->window); for (mbp = menu->buttonList ; mbp != (MENU_BUTTON *) 0 ; mbp = mbp->next) { /* draw the button labels' text */ XDrawString(display, mbp->window, menu->fontGC, (3 * menu->font->max_bounds.width) / 2, menu->font->max_bounds.ascent, mbp->label, strlen(mbp->label)); } /* XAllowEvents(display, SyncPointer, CurrentTime); */ XDefineCursor(display, RootWindow(display, screen), menu->cursor); XFlush(display); /* * establish the graphics context used to highlight boxes */ context_values.function = GXxor; /* draw by xor'ing src. and dest. */ context_values.foreground = BlackPixel(display,screen); context = XCreateGC(display, menu->window, GCForeground | GCFunction, &context_values); /* * main loop: track the window button box until the mouse button is released */ activeButtonNumber = 0; while(1) { XEvent event; XNextEvent(display, &event); /* fprintf(stderr, "event type: %d\n", event.type); */ switch(event.type) { case ButtonPress: /* if we are to (erroneously) start up the popup menu */ /* if (event.xbutton.button == Button1) { fprintf(stderr, "button press, xbutton.button = %d\n", event.xbutton.button); } */ break; case ButtonRelease: /* if we are releasing button 1 */ if (event.xbutton.button == Button1) { XUndefineCursor(display, RootWindow(display, screen)); XUnmapWindow(display, menu->window); return(activeButtonNumber); } /* else { fprintf(stderr, "other button release, xbutton.button = %d\n", event.xbutton.button); } */ break; case EnterNotify: /* traverse the button list */ for (mbp = menu->buttonList , i = 1 ; mbp != (MENU_BUTTON *) 0 ; mbp = mbp->next , i++) { /* if we entered this button */ if (mbp->window == event.xcrossing.window) { /* highlight the new button */ /* XFillRectangle(display, mbp->window, context, 0, 0, menu->buttonWidth, menu->buttonHeight); */ XFillRectangle(display, mbp->window, context, 0, 0, menu->font->max_bounds.width, menu->buttonHeight); activeButtonNumber = i; break; } } /* fprintf(stderr, "entering window %d\n", event.xcrossing.window); */ break; case LeaveNotify: /* traverse the button list */ for (mbp = menu->buttonList ; mbp != (MENU_BUTTON *) 0 ; mbp = mbp->next) { /* if we left this button */ if (mbp->window == event.xcrossing.window) { /* de-highlight the old button */ /* XFillRectangle(display, mbp->window, context, 0, 0, menu->buttonWidth, menu->buttonHeight); */ XFillRectangle(display, mbp->window, context, 0, 0, menu->font->max_bounds.width, menu->buttonHeight); activeButtonNumber = 0; break; } } /* fprintf(stderr, "leaving window %d\n", event.xcrossing.window); */ break; } } } main(argc, argv) int argc; char ** argv; { Display * display; /* the display */ int screen; /* the screen */ Window window; /* the main window */ PopupMenu * menu; /* the pop-up menu */ XSetWindowAttributes win_attrs; /* the window's attributes */ /* * open the default display */ if ((display = XOpenDisplay((char *) 0)) == (Display *) 0) { fprintf(stderr, "%s: can't open the default display\n", argv[0]); exit(1); } /* * create a window we can draw in */ screen = DefaultScreen(display); win_attrs.background_pixel = WhitePixel(display, screen); win_attrs.border_pixmap = None; win_attrs.border_pixel = BlackPixel(display, screen); /* black border */ win_attrs.event_mask = ButtonPressMask | ButtonReleaseMask /* | ButtonMotionMask */; window = XCreateWindow(display, /* parent: */ RootWindow(display, screen), /* x, y, width, height, border_width, depth, */ 0, 0, 100, 100, 1, 1, /* class, visual, valuemask, attributes */ InputOutput, DefaultVisual(display, screen), CWEventMask | CWBackPixel | CWBorderPixmap | CWBorderPixel /* | CWOverrideRedirect */ , &win_attrs); /* * map the main window */ XMapWindow(display, window); /* * next, create the popup menu and install the buttons */ menu = CreatePopupMenu(display, argv[0]); AddMenuItem(menu, "foo", (void (*)()) 0); AddMenuItem(menu, "bar", (void (*)()) 0); AddMenuItem(menu, "selection 1", (void (*)()) 0); AddMenuItem(menu, "agTYqj", (void (*)()) 0); InstallPopupMenu(display, screen, menu); #ifdef DOGRAB /* * establish a passive, synchronous grab on the window sponsoring the menu */ XGrabButton(display, Button1, 0, window, True, ButtonReleaseMask | EnterWindowMask | LeaveWindowMask, GrabModeSync, GrabModeAsync, None, None); #endif DOGRAB XFlush(display); /* * main loop: map and unmap the pop-up menu */ while(1) { XEvent event; XNextEvent(display, &event); /* fprintf(stderr, "event type: %d\n", event.type); */ if (event.type == ButtonPress && event.xbutton.button == Button1) { fprintf(stderr, "you selected button %d\n", DoPopupMenu(display, screen, menu, event.xbutton.x_root, event.xbutton.y_root)); } } /* XCloseDisplay(display); /* not really necessary */ } @//E*O*F popup.c// chmod u=rw,g=r,o=r popup.c echo x - popup.cursor sed 's/^@//' > "popup.cursor" <<'@//E*O*F popup.cursor//' #define popup_width 16 #define popup_height 16 #define popup_x_hot 15 #define popup_y_hot 4 static char popup_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x09, 0x80, 0x09, 0x78, 0x09, 0x04, 0x09, 0x08, 0x09, 0x06, 0x09, 0x04, 0x09, 0x03, 0x09, 0x01, 0xf9, 0x00, 0x09, 0x00, 0x0f, 0x00, 0x00, 0x00}; @//E*O*F popup.cursor// chmod u=rw,g=r,o=r popup.cursor exit 0 -- Tom Anderson, (206) 356-5895, ... uw-beaver!fluke!toma John Fluke Mfg. Co., Inc., POB C9090, Everett, Wa. 98206
mlandau@bbn.com (Matt Landau) (04/07/88)
In comp.windows.x (<3335@fluke.COM>), toma@tc.fluke.COM (Tom Anderson) writes: >p.s. - While I have your attention, does anyone know when Sun is going to get >around to improving the performance of the X server for Suns? I know they have >a bad case of NIH (Not-Invented-Here), and I also know they are planning to >support X via NeWS, but how hard can it be to support a real X server? Not to start any SunView vs X11 vs NeWS flamage or anything, but from what I've heard and seen, the merged NeWS/X11 server will BE a "real X server." The plan is in some sense to layer the NeWS and X11 protocol interpreters on top of a common window "forest" and a common kernel of imaging code. Given that the underlying event handling and imaging stuff should be the same for the NeWS and X sides of the server, the performance for NeWS and X11 programs should be comparable, except for the additional overhead that may be imposed by having to ship lots of X events over the server/client connection instead of loading some PostScript into the server and having more of the application handled directly for you. -- Matt Landau Waiting for a flash of enlightenment mlandau@bbn.com in all this blood and thunder
RWS@ZERMATT.LCS.MIT.EDU (Robert Scheifler) (04/09/88)
This isn't a problem with V11R2. The bug was in V11R1 with sending enter/leave events to them that shouldn't get them, and the bug is in your code, for not asking for them properly, using OwnerGrabButton. BTW, I can't demonstrate the cursor problem noted in your code, and you have made an error (that I've seen many people make) in not understanding what XOR means or how to use it. When I ran your code on an Apollo display, I got no highlighting. Fixes: 187,189d186 < * NOTE: The cursor mask is included; however, this shouldn't be < * necessary, since it should be inherited from the menu frame window. < * Something seems amiss in the way the server is handling this. 192d188 < win_attrs.cursor = menu->cursor; 199c195 < CWEventMask | CWBackPixel | CWBorderPixmap | CWBorderPixel | CWBackingStore | CWCursor, --- > CWEventMask | CWBackPixel | CWBorderPixmap | CWBorderPixel | CWBackingStore, 247c243 < context_values.foreground = BlackPixel(display,screen); --- > context_values.foreground = BlackPixel(display,screen) ^ WhitePixel(display, screen); 337c333 < win_attrs.event_mask = ButtonPressMask | ButtonReleaseMask /* | ButtonMotionMask */; --- > win_attrs.event_mask = ButtonPressMask | ButtonReleaseMask | OwnerGrabButtonMask /* | ButtonMotionMask */; 366c362 < ButtonReleaseMask | EnterWindowMask | LeaveWindowMask, --- > ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | OwnerGrabButtonMask,