#include "Manager.h" #include "Client.h" #include #define AllButtonMask ( Button1Mask | Button2Mask | Button3Mask \ | Button4Mask | Button5Mask ) #define ButtonMask ( ButtonPressMask | ButtonReleaseMask ) #define DragMask ( ButtonMask | ButtonMotionMask ) #define MenuMask ( ButtonMask | ButtonMotionMask | ExposureMask ) #define MenuGrabMask ( ButtonMask | ButtonMotionMask | StructureNotifyMask ) void WindowManager::eventButton(XButtonEvent *e) { if (e->button == Button3) { circulate(e->window == e->root); return; } Client *c = windowToClient(e->window); if (e->window == e->root) { if (e->button == Button1) menu(e); } else if (c) { c->eventButton(e); return; } } void WindowManager::circulate(Boolean activeFirst) { Client *c = 0; if (activeFirst) c = m_activeClient; if (!c) { int i, j; if (!m_activeClient) i = -1; else { for (i = 0; i < m_clients.count(); ++i) { if (m_clients.item(i) == m_activeClient) break; } if (i >= m_clients.count()-1) i = -1; } for (j = i + 1; (!m_clients.item(j)->isNormal() || m_clients.item(j)->isTransient()); ++j) { if (j >= m_clients.count() - 1) j = -1; if (j == i) return; // no suitable clients } c = m_clients.item(j); } c->activateAndWarp(); } void Client::activateAndWarp() { mapRaised(); ensureVisible(); XWarpPointer(display(), None, parent(), 0, 0, 0, 0, m_border->xIndent() / 2, m_border->xIndent() + 8); activate(); } void Client::eventButton(XButtonEvent *e) { if (e->type != ButtonPress) return; mapRaised(); if (e->button == Button1) { if (m_border->hasWindow(e->window)) { m_border->eventButton(e); } } if (!isNormal() || isActive() || e->send_event) return; activate(); } static int nobuttons(XButtonEvent *e) // straight outta 9wm { int state; state = (e->state & AllButtonMask); return (e->type == ButtonRelease) && (state & (state - 1)) == 0; } int WindowManager::attemptGrab(Window w, Window constrain, int mask, int t) { int status; if (t == 0) t = timestamp(False); status = XGrabPointer(display(), w, False, mask, GrabModeAsync, GrabModeAsync, constrain, None, t); return status; } void WindowManager::releaseGrab(XButtonEvent *e) { XEvent ev; if (!nobuttons(e)) { for (;;) { XMaskEvent(display(), ButtonMask | ButtonMotionMask, &ev); if (ev.type == MotionNotify) continue; e = &ev.xbutton; if (nobuttons(e)) break; } } XUngrabPointer(display(), e->time); m_currentTime = e->time; } void WindowManager::menu(XButtonEvent *e) { if (e->window == m_menuWindow) return; int i; ClientList clients; Boolean allowExit = False; #define MENU_LABEL(n) ((n)==0 ? m_menuCreateLabel : \ (allowExit && ((n) > clients.count())) ? "[Exit wm2]" \ : clients.item((n)-1)->label()) for (i = 0; i < m_hiddenClients.count(); ++i) { clients.append(m_hiddenClients.item(i)); } int nh = clients.count() + 1; if (CONFIG_EVERYTHING_ON_ROOT_MENU) { for (i = 0; i < m_clients.count(); ++i) { if (m_clients.item(i)->isNormal()) { clients.append(m_clients.item(i)); } } } int n = clients.count() + 1; int mx = DisplayWidth (display(), m_screenNumber) - 1; int my = DisplayHeight(display(), m_screenNumber) - 1; allowExit = ((e->x > mx-3) && (e->y > my-3)); if (allowExit) n += 1; int width, maxWidth = 10; for (i = 0; i < n; ++i) { width = XTextWidth(m_menuFont, MENU_LABEL(i), strlen(MENU_LABEL(i))); if (width > maxWidth) maxWidth = width; } maxWidth += 32; int selecting = -1, prev = -1; int entryHeight = m_menuFont->ascent + m_menuFont->descent + 4; int totalHeight = entryHeight * n + 13; int x = e->x - maxWidth/2; int y = e->y - 2; Boolean warp = False; if (x < 0) { e->x -= x; x = 0; warp = True; } else if (x + maxWidth >= mx) { e->x -= x + maxWidth - mx; x = mx - maxWidth; warp = True; } if (y < 0) { e->y -= y; y = 0; warp = True; } else if (y + totalHeight >= my) { e->y -= y + totalHeight - my; y = my - totalHeight; warp = True; } if (warp) XWarpPointer(display(), None, root(), None, None, None, None, e->x, e->y); XMoveResizeWindow(display(), m_menuWindow, x, y, maxWidth, totalHeight); XSelectInput(display(), m_menuWindow, MenuMask); XMapRaised(display(), m_menuWindow); if (attemptGrab(m_menuWindow, None, MenuGrabMask, e->time) != GrabSuccess){ XUnmapWindow(display(), m_menuWindow); return; } Boolean done = False; Boolean drawn = False; XEvent event; while (!done) { XMaskEvent(display(), MenuMask, &event); switch (event.type) { default: fprintf(stderr, "wm2: unknown event type %d\n", event.type); break; case ButtonPress: break; case ButtonRelease: if (drawn) { if (event.xbutton.button != e->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 (x < 0 || x > maxWidth || y < -3) i = -1; else if (i < 0 || i >= n) i = -1; } else { selecting = -1; } if (!nobuttons(&event.xbutton)) i = -1; releaseGrab(&event.xbutton); XUnmapWindow(display(), m_menuWindow); selecting = i; done = True; break; case MotionNotify: if (!drawn) 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 (x < 0 || x > maxWidth || y < -3) selecting = -1; else if (selecting < 0 || selecting > n) selecting = -1; if (selecting == prev) break; if (prev >= 0 && prev < n) { XFillRectangle(display(), m_menuWindow, m_menuGC, 4, prev * entryHeight + 9, maxWidth - 8, entryHeight); } if (selecting >= 0 && selecting < n) { XFillRectangle(display(), m_menuWindow, m_menuGC, 4, selecting * entryHeight + 9, maxWidth - 8, entryHeight); } break; case Expose: XClearWindow(display(), m_menuWindow); XDrawRectangle(display(), m_menuWindow, m_menuGC, 2, 7, maxWidth - 5, totalHeight - 10); for (i = 0; i < n; ++i) { int dx = XTextWidth(m_menuFont, MENU_LABEL(i), strlen(MENU_LABEL(i))); int dy = i * entryHeight + m_menuFont->ascent + 10; if (i >= nh) { XDrawString(display(), m_menuWindow, m_menuGC, maxWidth - 8 - dx, dy, MENU_LABEL(i), strlen(MENU_LABEL(i))); } else { XDrawString(display(), m_menuWindow, m_menuGC, 8, dy, MENU_LABEL(i), strlen(MENU_LABEL(i))); } } if (selecting >= 0 && selecting < n) { XFillRectangle(display(), m_menuWindow, m_menuGC, 4, selecting * entryHeight + 9, maxWidth - 8, entryHeight); } drawn = True; } } if (selecting == n-1 && allowExit) { m_signalled = True; return; } if (selecting >= 0) { if (selecting == 0) { spawn(); } else if (selecting < nh) { clients.item(selecting - 1)->unhide(True); } else if (selecting < n) { if (CONFIG_CLICK_TO_FOCUS) { clients.item(selecting - 1)->activate(); } else { clients.item(selecting - 1)->mapRaised(); } clients.item(selecting - 1)->ensureVisible(); } } clients.remove_all(); return; } void WindowManager::showGeometry(int x, int y) { char string[20]; snprintf(string, sizeof(string), "%d %d\n", x, y); int width = XTextWidth(m_menuFont, string, strlen(string)) + 8; int height = m_menuFont->ascent + m_menuFont->descent + 8; int mx = DisplayWidth (display(), m_screenNumber) - 1; int my = DisplayHeight(display(), m_screenNumber) - 1; XMoveResizeWindow(display(), m_menuWindow, (mx - width) / 2, (my - height) / 2, width, height); XClearWindow(display(), m_menuWindow); XMapRaised(display(), m_menuWindow); XDrawString(display(), m_menuWindow, m_menuGC, 4, 4 + m_menuFont->ascent, string, strlen(string)); } void WindowManager::removeGeometry() { XUnmapWindow(display(), m_menuWindow); } void Client::move(XButtonEvent *e) { int x = -1, y = -1, xoff, yoff; Boolean done = False; if (m_windowManager->attemptGrab (root(), None, DragMask, e->time) != GrabSuccess) { return; } xoff = m_border->xIndent() - e->x; yoff = m_border->yIndent() - e->y; XEvent event; Boolean found; Boolean doSomething = False; struct timeval sleepval; while (!done) { found = False; while (XCheckMaskEvent(display(), DragMask | ExposureMask, &event)) { found = True; if (event.type != MotionNotify) break; } if (!found) { sleepval.tv_sec = 0; sleepval.tv_usec = 50000; select(0, 0, 0, 0, &sleepval); continue; } switch (event.type) { default: fprintf(stderr, "wm2: unknown event type %d\n", event.type); break; case Expose: m_windowManager->eventExposure(&event.xexpose); break; case ButtonPress: // don't like this XUngrabPointer(display(), event.xbutton.time); doSomething = False; done = True; break; case ButtonRelease: x = event.xbutton.x; y = event.xbutton.y; if (!nobuttons(&event.xbutton)) doSomething = False; // XUngrabPointer(display(), event.xbutton.time); m_windowManager->releaseGrab(&event.xbutton); done = True; break; case MotionNotify: x = event.xbutton.x; y = event.xbutton.y; if (x + xoff != m_x || y + yoff != m_y) { windowManager()->showGeometry(x + xoff, y + yoff); m_border->moveTo(x + xoff, y + yoff); doSomething = True; } break; } } windowManager()->removeGeometry(); if (x >= 0 && doSomething) { m_x = x + xoff; m_y = y + yoff; } if (CONFIG_CLICK_TO_FOCUS) activate(); m_border->moveTo(m_x, m_y); sendConfigureNotify(); } void Client::fixResizeDimensions(int &w, int &h, int &dw, int &dh) { if (w < 50) w = 50; if (h < 50) h = 50; if (m_sizeHints.flags & PResizeInc) { w = m_minWidth + (((w - m_minWidth) / m_sizeHints.width_inc) * m_sizeHints.width_inc); h = m_minHeight + (((h - m_minHeight) / m_sizeHints.height_inc) * m_sizeHints.height_inc); dw = (w - m_minWidth) / m_sizeHints.width_inc; dh = (h - m_minHeight) / m_sizeHints.height_inc; } else { dw = w; dh = h; } if (m_sizeHints.flags & PMaxSize) { if (w > m_sizeHints.max_width) w = m_sizeHints.max_width; if (h > m_sizeHints.max_height) h = m_sizeHints.max_height; } if (w < m_minWidth) w = m_minWidth; if (h < m_minHeight) h = m_minHeight; } void Client::resize(XButtonEvent *e, Boolean horizontal, Boolean vertical) { if (isFixedSize()) return; if (m_windowManager->attemptGrab (root(), None, DragMask, e->time) != GrabSuccess) { return; } if (vertical && horizontal) m_windowManager->installCursor(WindowManager::DownrightCursor); else if (vertical) m_windowManager->installCursor(WindowManager::DownCursor); else m_windowManager->installCursor(WindowManager::RightCursor); Window dummy; XTranslateCoordinates(display(), e->window, parent(), e->x, e->y, &e->x, &e->y, &dummy); int xorig = e->x; int yorig = e->y; int x = xorig; int y = yorig; int w = m_w, h = m_h; int prevW, prevH; int dw, dh; XEvent event; Boolean found; Boolean doSomething = False; Boolean done = False; struct timeval sleepval; while (!done) { found = False; while (XCheckMaskEvent(display(), DragMask | ExposureMask, &event)) { found = True; if (event.type != MotionNotify) break; } if (!found) { sleepval.tv_sec = 0; sleepval.tv_usec = 50000; select(0, 0, 0, 0, &sleepval); continue; } switch (event.type) { default: fprintf(stderr, "wm2: unknown event type %d\n", event.type); break; case Expose: m_windowManager->eventExposure(&event.xexpose); break; case ButtonPress: // don't like this XUngrabPointer(display(), event.xbutton.time); done = True; break; case ButtonRelease: x = event.xbutton.x; y = event.xbutton.y; if (!nobuttons(&event.xbutton)) x = -1; m_windowManager->releaseGrab(&event.xbutton); done = True; break; case MotionNotify: x = event.xbutton.x; y = event.xbutton.y; if (vertical && horizontal) { prevH = h; h = y - m_y; prevW = w; w = x - m_x; fixResizeDimensions(w, h, dw, dh); if (h == prevH && w == prevW) break; m_border->configure(m_x, m_y, w, h, CWWidth | CWHeight, 0); windowManager()->showGeometry(dw, dh); doSomething = True; } else if (vertical) { prevH = h; h = y - m_y; fixResizeDimensions(w, h, dw, dh); if (h == prevH) break; m_border->configure(m_x, m_y, w, h, CWHeight, 0); windowManager()->showGeometry(dw, dh); doSomething = True; } else { prevW = w; w = x - m_x; fixResizeDimensions(w, h, dw, dh); if (w == prevW) break; m_border->configure(m_x, m_y, w, h, CWWidth, 0); windowManager()->showGeometry(dw, dh); doSomething = True; } break; } } if (doSomething) { windowManager()->removeGeometry(); if (vertical && horizontal) { m_w = x - m_x; m_h = y - m_y; fixResizeDimensions(m_w, m_h, dw, dh); m_border->configure(m_x, m_y, m_w, m_h, CWWidth|CWHeight, 0, True); } else if (vertical) { m_h = y - m_y; fixResizeDimensions(m_w, m_h, dw, dh); m_border->configure(m_x, m_y, m_w, m_h, CWHeight, 0, True); } else { m_w = x - m_x; fixResizeDimensions(m_w, m_h, dw, dh); m_border->configure(m_x, m_y, m_w, m_h, CWWidth, 0, True); } XMoveResizeWindow(display(), m_window, m_border->xIndent(), m_border->yIndent(), m_w, m_h); sendConfigureNotify(); } m_windowManager->installCursor(WindowManager::NormalCursor); } void Client::moveOrResize(XButtonEvent *e) { if (e->x < m_border->xIndent() && e->y > m_h) { resize(e, False, True); } else if (e->y < m_border->yIndent() && e->x > m_w + m_border->xIndent() - m_border->yIndent()) { //hack resize(e, True, False); } else { move(e); } } void Border::eventButton(XButtonEvent *e) { if (e->window == m_parent) { if (!m_client->isActive()) return; if (isTransient()) { if (e->x >= xIndent() && e->y >= yIndent()) { return; } else { m_client->move(e); return; } } m_client->moveOrResize(e); return; } else if (e->window == m_tab) { m_client->move(e); return; } if (e->window == m_resize) { m_client->resize(e, True, True); return; } if (e->window != m_button || e->type == ButtonRelease) return; if (windowManager()->attemptGrab(m_button, None, MenuGrabMask, e->time) != GrabSuccess) { return; } XEvent event; Boolean found; Boolean done = False; struct timeval sleepval; unsigned long tdiff = 0L; int x = e->x; int y = e->y; int action = 1; int buttonSize = m_tabWidth - TAB_TOP_HEIGHT*2 - 4; XFillRectangle(display(), m_button, m_drawGC, 0, 0, buttonSize, buttonSize); while (!done) { found = False; if (tdiff > CONFIG_DESTROY_WINDOW_DELAY && action == 1) { windowManager()->installCursor(WindowManager::DeleteCursor); action = 2; } while (XCheckMaskEvent(display(), MenuMask, &event)) { found = True; if (event.type != MotionNotify) break; } if (!found) { sleepval.tv_sec = 0; sleepval.tv_usec = 50000; select(0, 0, 0, 0, &sleepval); tdiff += 50; continue; } switch (event.type) { default: fprintf(stderr, "wm2: unknown event type %d\n", event.type); break; case Expose: windowManager()->eventExposure(&event.xexpose); break; case ButtonPress: break; case ButtonRelease: if (!nobuttons(&event.xbutton)) { action = 0; } if (x < 0 || y < 0 || x >= buttonSize || y >= buttonSize) { action = 0; } windowManager()->releaseGrab(&event.xbutton); done = True; break; case MotionNotify: tdiff = event.xmotion.time - e->time; if (tdiff > 5000L) tdiff = 5001L; // in case of overflow! x = event.xmotion.x; y = event.xmotion.y; if (action == 0 || action == 2) { if (x < 0 || y < 0 || x >= buttonSize || y >= buttonSize) { windowManager()->installCursor(WindowManager::NormalCursor); action = 0; } else { windowManager()->installCursor(WindowManager::DeleteCursor); action = 2; } } break; } } XClearWindow(display(), m_button); windowManager()->installCursor(WindowManager::NormalCursor); if (tdiff > 5000L) { // do nothing, they dithered too long return; } if (action == 1) m_client->hide(); else if (action == 2) m_client->kill(); }