void ungrab(XButtonEvent *e) { XEvent ev; if(!nobuttons(e)) for(;;){ XMaskEvent(dpy, ButtonMask | ButtonMotionMask, &ev); if(ev.type == MotionNotify) continue; e = &ev.xbutton; if(nobuttons(e)) break; } XUngrabPointer(dpy, e->time); curtime = e->time; }
int menuhit(XButtonEvent *e, Menu *m) { XEvent ev; int i, n, cur, old, wide, high, status, drawn, warp; int x, y, dx, dy, xmax, ymax; ScreenInfo *s; if(font == 0) return -1; s = getscreen(e->root); if(s == 0 || e->window == s->menuwin) /* ugly event mangling */ return -1; dx = 0; for(n = 0; m->item[n]; n++){ wide = XTextWidth(font, m->item[n], strlen(m->item[n])) + 4; if(wide > dx) dx = wide; } wide = dx; cur = m->lasthit; if(cur >= n) cur = n - 1; high = font->ascent + font->descent + 1; dy = n*high; x = e->x - wide/2; y = e->y - cur*high - high/2; warp = 0; xmax = DisplayWidth(dpy, s->num); ymax = DisplayHeight(dpy, s->num); if(x < 0){ e->x -= x; x = 0; warp++; } if(x+wide >= xmax){ e->x -= x+wide-xmax; x = xmax-wide; warp++; } if(y < 0){ e->y -= y; y = 0; warp++; } if(y+dy >= ymax){ e->y -= y+dy-ymax; y = ymax-dy; warp++; } if(warp) setmouse(e->x, e->y, s); XMoveResizeWindow(dpy, s->menuwin, x, y, dx, dy); XSelectInput(dpy, s->menuwin, MenuMask); XMapRaised(dpy, s->menuwin); status = grab(s->menuwin, None, MenuGrabMask, None, e->time); if(status != GrabSuccess){ /* graberror("menuhit", status); */ XUnmapWindow(dpy, s->menuwin); return -1; } drawn = 0; for(;;){ XMaskEvent(dpy, MenuMask, &ev); switch (ev.type){ default: fprintf(stderr, "rio: menuhit: unknown ev.type %d\n", ev.type); break; case ButtonPress: break; case ButtonRelease: if(ev.xbutton.button != e->button) break; x = ev.xbutton.x; y = ev.xbutton.y; i = y/high; if(cur >= 0 && y >= cur*high-3 && y < (cur+1)*high+3) i = cur; if(x < 0 || x > wide || y < -3) i = -1; else if(i < 0 || i >= n) i = -1; else m->lasthit = i; if(!nobuttons(&ev.xbutton)) i = -1; ungrab(&ev.xbutton); XUnmapWindow(dpy, s->menuwin); return i; case MotionNotify: if(!drawn) break; x = ev.xbutton.x; y = ev.xbutton.y; old = cur; cur = y/high; if(old >= 0 && y >= old*high-3 && y < (old+1)*high+3) cur = old; if(x < 0 || x > wide || y < -3) cur = -1; else if(cur < 0 || cur >= n) cur = -1; if(cur == old) break; if(old >= 0 && old < n) drawstring(dpy, s, m, wide, high, old, 0); if(cur >= 0 && cur < n) drawstring(dpy, s, m, wide, high, cur, 1); break; case Expose: XClearWindow(dpy, s->menuwin); for(i = 0; i < n; i++) drawstring(dpy, s, m, wide, high, i, cur==i); drawn = 1; } } }
int Menu::getSelection() { m_items = getItems(&m_nItems, &m_nHidden); XButtonEvent *xbev = (XButtonEvent *)m_event; // KeyEvent is similar enough if (xbev->window == m_window[screen()] || m_nItems == 0) return -1; int width, maxWidth = 10; for (int i = 0; i < m_nItems; i++) { width = getTextWidth(m_items[i], STRLEN_MITEMS(i)); if (width > maxWidth) maxWidth = width; } maxWidth += 32; Boolean isKeyboardMenu = isKeyboardMenuEvent(m_event); int selecting = isKeyboardMenu ? 0 : -1, prev = -1; #ifdef CONFIG_USE_XFT int entryHeight = m_font->ascent + m_font->descent + 4; #else int entryHeight = m_font[screen()]->ascent + m_font[screen()]->descent + 4; #endif int totalHeight = entryHeight * m_nItems + 13; int mx = DisplayWidth (display(), screen()) - 1; int my = DisplayHeight(display(), screen()) - 1; int x, y; if (isKeyboardMenu) { x = mx / 2 - maxWidth / 2; y = my / 2 - totalHeight / 2; } else { x = xbev->x - maxWidth/2; y = xbev->y - 2; Boolean warp = False; if (x < 0) { xbev->x -= x; x = 0; warp = True; } else if (x + maxWidth >= mx) { xbev->x -= x + maxWidth - mx; x = mx - maxWidth; warp = True; } if (y < 0) { xbev->y -= y; y = 0; warp = True; } else if (y + totalHeight >= my) { xbev->y -= y + totalHeight - my; y = my - totalHeight; warp = True; } if (warp) XWarpPointer(display(), None, root(), None, None, None, None, xbev->x, xbev->y); } XMoveResizeWindow(display(), m_window[screen()], x, y, maxWidth, totalHeight); XSelectInput(display(), m_window[screen()], MenuMask); XMapRaised(display(), m_window[screen()]); if (m_windowManager->attemptGrab(m_window[screen()], None, MenuGrabMask, xbev->time) != GrabSuccess) { XUnmapWindow(display(), m_window[screen()]); return -1; } if (isKeyboardMenu) { if (m_windowManager->attemptGrabKey(m_window[screen()], xbev->time) != GrabSuccess) { XUnmapWindow(display(), m_window[screen()]); return -1; } } Boolean done = False; Boolean drawn = False; XEvent event; struct timeval sleepval; unsigned long tdiff = 0L; Boolean speculating = False; Boolean foundEvent; while (!done) { int i; foundEvent = False; if (CONFIG_FEEDBACK_DELAY >= 0 && tdiff > (unsigned long)CONFIG_FEEDBACK_DELAY && !isKeyboardMenu && // removeFeedback didn't seem to work for it !speculating) { if (selecting >= 0 && selecting < m_nItems) { raiseFeedbackLevel(selecting); XRaiseWindow(display(), m_window[screen()]); } speculating = True; } //!!! MenuMask | ??? suggests MenuMask is wrong while (XCheckMaskEvent (display(), MenuMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask, &event)) { foundEvent = True; if (event.type != MotionNotify) break; } if (!foundEvent) { sleepval.tv_sec = 0; sleepval.tv_usec = 10000; select(0, 0, 0, 0, &sleepval); tdiff += 10; continue; } switch (event.type) { case ButtonPress: break; case ButtonRelease: if (isKeyboardMenu) break; if (drawn) { if (event.xbutton.button != xbev->button) break; x = event.xbutton.x; y = event.xbutton.y - 11; i = y / entryHeight; if (selecting >= 0 && y >= selecting * entryHeight - 3 && y <= (selecting + 1) * entryHeight - 3) i = selecting; if (m_hasSubmenus && (i >= 0 && i < m_nHidden)) i = -i; if (x < 0 || x > maxWidth || y < -3) i = -1; else if (i < 0 || i >= m_nItems) i = -1; } else { i = -1; } if (!nobuttons(&event.xbutton)) i = -1; m_windowManager->releaseGrab(&event.xbutton); XUnmapWindow(display(), m_window[screen()]); selecting = i; done = True; break; case MotionNotify: if (!drawn || isKeyboardMenu) break; x = event.xbutton.x; y = event.xbutton.y - 11; prev = selecting; selecting = y / entryHeight; if (prev >= 0 && y >= prev * entryHeight - 3 && y <= (prev+1) * entryHeight - 3) selecting = prev; if (m_hasSubmenus && (selecting >= 0 && selecting < m_nHidden) && x >= maxWidth-32 && x < maxWidth) { xbev->x += event.xbutton.x - 32; xbev->y += event.xbutton.y; createSubmenu ((XEvent *)xbev, selecting); done = True; break; } if (x < 0 || x > maxWidth || y < -3) selecting = -1; else if (selecting < 0 || selecting > m_nItems) selecting = -1; if (selecting == prev) break; tdiff = 0; speculating = False; if (prev >= 0 && prev < m_nItems) { removeFeedback(prev, speculating); XFillRectangle(display(), m_window[screen()], m_menuGC[screen()], 4, prev * entryHeight + 9, maxWidth - 8, entryHeight); } if (selecting >= 0 && selecting < m_nItems) { showFeedback(selecting); XRaiseWindow(display(), m_window[screen()]); XFillRectangle(display(), m_window[screen()], m_menuGC[screen()], 4, selecting * entryHeight + 9, maxWidth - 8, entryHeight); } break; case Expose: if (CONFIG_MAD_FEEDBACK && event.xexpose.window != m_window[screen()]) { m_windowManager->dispatchEvent(&event); break; } XClearWindow(display(), m_window[screen()]); XDrawRectangle(display(), m_window[screen()], m_menuGC[screen()], 2, 7, maxWidth - 5, totalHeight - 10); for (i = 0; i < m_nItems; i++) { int dx = getTextWidth(m_items[i], STRLEN_MITEMS(i)); #ifdef CONFIG_USE_XFT int dy = i * entryHeight + m_font->ascent + 10; #else int dy = i * entryHeight + m_font[screen()]->ascent + 10; #endif if (i >= m_nHidden) { #ifdef CONFIG_USE_XFT XftDrawStringUtf8(m_xftDraw[screen()], &m_xftColour[screen()], m_font, maxWidth - 8 - dx, dy, (FcChar8 *)m_items[i], STRLEN_MITEMS(i)); #else XDrawString(display(), m_window[screen()], Border::drawGC(m_windowManager,screen()), maxWidth - 8 - dx, dy, m_items[i], STRLEN_MITEMS(i)); #endif } else { #ifdef CONFIG_USE_XFT XftDrawStringUtf8(m_xftDraw[screen()], &m_xftColour[screen()], m_font, 8, dy, (FcChar8 *)m_items[i], STRLEN_MITEMS(i)); #else XDrawString(display(), m_window[screen()], Border::drawGC(m_windowManager,screen()), 8, dy, m_items[i], STRLEN_MITEMS(i)); #endif } } if (selecting >= 0 && selecting < m_nItems) { XFillRectangle(display(), m_window[screen()], m_menuGC[screen()], 4, selecting * entryHeight + 9, maxWidth - 8, entryHeight); } drawn = True; break; case KeyPress: { if (!isKeyboardMenu) break; KeySym key = XKeycodeToKeysym(display(), event.xkey.keycode, 0); if (key == CONFIG_MENU_SELECT_KEY) { if (!drawn) selecting = -1; if (m_hasSubmenus && selecting >= 0 && selecting < m_nHidden) { createSubmenu((XEvent *)xbev, selecting); selecting = -1; } m_windowManager->releaseGrabKeyMode(&event.xkey); XUnmapWindow(display(), m_window[screen()]); done = True; break; } else if (key == CONFIG_MENU_CANCEL_KEY) { m_windowManager->releaseGrabKeyMode(&event.xkey); XUnmapWindow(display(), m_window[screen()]); if (selecting >= 0) removeFeedback(selecting, speculating); selecting = -1; done = True; break; } else if (key != CONFIG_MENU_UP_KEY && key != CONFIG_MENU_DOWN_KEY) { break; } if (!drawn) break; prev = selecting; if (key == CONFIG_MENU_UP_KEY) { if (prev <= 0) selecting = m_nItems - 1; else selecting = prev - 1; } else { if (prev == m_nItems - 1 || prev < 0) selecting = 0; else selecting = prev + 1; } tdiff = 0; speculating = False; if (prev >= 0 && prev < m_nItems) { removeFeedback(prev, speculating); XFillRectangle(display(), m_window[screen()], m_menuGC[screen()], 4, prev * entryHeight + 9, maxWidth - 8, entryHeight); } if (selecting >= 0 && selecting < m_nItems) { showFeedback(selecting); XRaiseWindow(display(), m_window[screen()]); XFillRectangle(display(), m_window[screen()], m_menuGC[screen()], 4, selecting * entryHeight + 9, maxWidth - 8, entryHeight); } break; } case KeyRelease: break; default: if (event.xmap.window == m_window[screen()]) break; m_windowManager->dispatchEvent(&event); } } if (selecting >= 0) removeFeedback(selecting, speculating); return selecting; }