/* * switcher.cc * * Copyright (C) 1995, 1996, 1997, 1997, 1998, 1999, 2000, 2001, 2002 Kenichi Kourai * Copyright (C) 1999, 2000, 2001, 2002 Luiz Blanes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with blwm; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "main.h" #include "blwm.h" #include "switcher.h" #include "blwmrc.h" #include "timer.h" #include "key.h" #include "focus_mgr.h" #include "timer.h" Timer* TaskSwitcher::swTimer; TaskSwitcher::TaskSwitcher() { XSetWindowAttributes attributes; unsigned long valueMask; /* * Create frame window. */ attributes.background_pixel = SwitcherColor.pixel; attributes.override_redirect = True; attributes.event_mask = KeyPressMask | KeyReleaseMask | ExposureMask; valueMask = CWBackPixel | CWOverrideRedirect | CWEventMask; frame = XCreateWindow(display, root, 0, 0, 10, 10, 0, CopyFromParent, InputOutput, CopyFromParent, valueMask, &attributes); /* * Create the window displaying title name. */ valueMask = CWBackPixel; titleFrame = XCreateWindow(display, frame, 14, 0, 322, 24, 0, CopyFromParent, InputOutput, CopyFromParent, valueMask, &attributes); title = XCreateWindow(display, titleFrame, 2, 2, 318, 20, 0, CopyFromParent, InputOutput, CopyFromParent, valueMask, &attributes); if (SwitcherImage) { imgSwitcher = CreateImageFromFile(SwitcherImage, swTimer); if (imgSwitcher) { imgSwitcher->SetBackground(frame); imgTitle = NULL; } else { delete [] SwitcherImage; SwitcherImage = NULL; } } iterMap = new List::Iterator(&focusMgr.GetMapList()); iterUnmap = new List::Iterator(&focusMgr.GetUnmapList()); } TaskSwitcher::~TaskSwitcher() { XDestroyWindow(display, frame); if (SwitcherImage) { BlImage::Destroy(imgSwitcher); BlImage::Destroy(imgTitle); } } void TaskSwitcher::MapSwitcher(int winNum) { int col = (winNum + 6) / 7; ASSERT(rootBlwm); Rect rcRoot = rootBlwm->GetRect(); rc = Rect((rcRoot.width - BASE_WIDTH) / 2, (rcRoot.height - BASE_HEIGHT - col * 43) / 2, BASE_WIDTH, BASE_HEIGHT + col * 43); XMoveResizeWindow(display, frame, rc.x, rc.y, rc.width, rc.height); XMoveWindow(display, titleFrame, 14, 29 + col * 43); if (SwitcherImage) { BlImage::Destroy(imgTitle); imgTitle = imgSwitcher->GetOffsetImage(Point(14 + 2, 29 + col * 43 + 2)); imgTitle->SetBackground(title); } XMapWindow(display, titleFrame); XMapWindow(display, title); XMapRaised(display, frame); } /* * SwitchTask -- * Display task switcher and switch focus window. * This routine must be called by modifier + code. * mod is with LockMask. */ void TaskSwitcher::SwitchTask(Bool forward, KeyCode code, unsigned int mod) { int winNum = focusMgr.GetAllNum(); Blwm* blWm; if (winNum == 0) return; else if (winNum == 1) { blWm = focusMgr.Top(); if (blWm == NULL) blWm = focusMgr.TopUnmapList(); blWm->SetFocus(); if (blWm->CheckMapped()) blWm->RaiseWindow(True); else blWm->RestoreWindow(); blWm->AdjustPage(); return; } direct = forward; MapSwitcher(winNum); XSetInputFocus(display, frame, RevertToParent, CurrentTime); EventLoop(winNum, code, mod); } void TaskSwitcher::EventLoop(int winNum, KeyCode code, unsigned int mod) { Blwm* blWm = GetFirstFocus(); int focusWin; XEvent ev; int fd = ConnectionNumber(display); fd_set fds; struct timeval tm; if (Blwm::focusBlwm == rootBlwm) focusWin = 0; else focusWin = direct ? 1 : winNum - 1; XGrabServer(display); while (1) { FD_ZERO(&fds); FD_SET(fd, &fds); while (XPending(display) != 0) { XNextEvent(display, &ev); switch (ev.type) { case Expose: DrawTaskSwitcher(); ASSERT(blWm); DrawTitle(blWm->name); DrawPixmaps(winNum); DrawRect(focusWin, winNum); while (XCheckWindowEvent(display, ev.xexpose.window, ExposureMask, &ev)) ; break; case KeyPress: if (ev.xkey.keycode == code && ev.xkey.state == mod) { blWm = direct ? GetNextFocus() : GetPrevFocus(); ASSERT(blWm); DrawTitle(blWm->name); if (direct) { focusWin++; if (focusWin == winNum) focusWin = 0; } else { focusWin--; if (focusWin == -1) focusWin = winNum - 1; } DrawRect(focusWin, winNum); } if (XKeycodeToKeysym(display, ev.xkey.keycode, 0) == XK_Escape) { XUnmapWindow(display, frame); XUngrabServer(display); if (ClickToFocus) XSetInputFocus(display, blWm->GetWin(), RevertToParent, CurrentTime); else XSetInputFocus(display, root, RevertToParent, CurrentTime); return; } break; case KeyRelease: if (mod & ShiftMask) { if (XKeycodeToKeysym(display, ev.xkey.keycode, 0) != XK_Shift_L && XKeycodeToKeysym(display, ev.xkey.keycode, 0) != XK_Shift_R) break; } else if (mod & ControlMask) { if (XKeycodeToKeysym(display, ev.xkey.keycode, 0) != XK_Control_L && XKeycodeToKeysym(display, ev.xkey.keycode, 0) != XK_Control_R) break; } else if (mod & ShortCutKey::getAltMask()) { if (XKeycodeToKeysym(display, ev.xkey.keycode, 0) != XK_Alt_L && XKeycodeToKeysym(display, ev.xkey.keycode, 0) != XK_Alt_R) break; } else if (mod & ShortCutKey::getMetaMask()) { if (XKeycodeToKeysym(display, ev.xkey.keycode, 0) != XK_Meta_L && XKeycodeToKeysym(display, ev.xkey.keycode, 0) != XK_Meta_R) break; } XUnmapWindow(display, frame); XUngrabServer(display); if (blWm->CheckMapped()) { blWm->SetFocus(); /* * If a new active window has already have a focus, XSetInputFocus * doesn't call and the window with a mouse pointer gets an input * focus. To avoid this problem, blwm calls XSetInputFocus below. */ XSetInputFocus(display, blWm->GetWin(), RevertToParent, CurrentTime); blWm->RaiseWindow(True); } else blWm->RestoreWindow(); blWm->AdjustPage(); return; } swTimer->CheckTimeout(&tm); } if (!swTimer->CheckTimeout(&tm)) { tm.tv_sec = 1; tm.tv_usec = 0; XFlush(display); } #if defined(__hpux) && !defined(_XPG4_EXTENDED) if (select(fd + 1, (int *)&fds, 0, 0, &tm) == 0) { // timeout #else if (select(fd + 1, &fds, 0, 0, &tm) == 0) { // timeout #endif swTimer->CheckTimeout(&tm); } } } /* * Called firstly once */ Blwm* TaskSwitcher::GetFirstFocus() { Blwm* blWm; blWm = iterMap->GetHead(); if (blWm) { LookMapList = True; if (Blwm::focusBlwm == blWm) blWm = direct ? GetNextFocus() : GetPrevFocus(); } else { blWm = iterUnmap->GetHead(); ASSERT(blWm); LookMapList = False; } return blWm; } /* * Get next window in the focus stack, including unmapped windows */ Blwm* TaskSwitcher::GetNextFocus() { Blwm* blWm; if (LookMapList) { blWm = iterMap->GetNext(); if (blWm == NULL) { blWm = iterUnmap->GetHead(); if (blWm) LookMapList = False; else { blWm = iterMap->GetHead(); ASSERT(blWm); } } } else { blWm = iterUnmap->GetNext(); if (blWm == NULL) { blWm = iterMap->GetHead(); if (blWm) LookMapList = True; else { blWm = iterUnmap->GetHead(); ASSERT(blWm); } } } return blWm; } /* * Get prev window in the focus stack, including unmapped windows */ Blwm* TaskSwitcher::GetPrevFocus() { Blwm* blWm; if (LookMapList) { blWm = iterMap->GetPrev(); if (blWm == NULL) { blWm = iterUnmap->GetTail(); if (blWm) LookMapList = False; else { blWm = iterMap->GetTail(); ASSERT(blWm); } } } else { blWm = iterUnmap->GetPrev(); if (blWm == NULL) { blWm = iterMap->GetTail(); if (blWm) LookMapList = True; else { blWm = iterUnmap->GetTail(); ASSERT(blWm); } } } return blWm; } /* * DrawTaskSwitcher -- * Draw task switcher. */ void TaskSwitcher::DrawTaskSwitcher() { XPoint xp[3]; xp[0].x = rc.width - 2; xp[0].y = 0; xp[1].x = 0; xp[1].y = 0; xp[2].x = 0; xp[2].y = rc.height - 2; XSetForeground(display, gc, gray.pixel); XDrawLines(display, frame, gc, xp, 3, CoordModeOrigin); xp[0].x = rc.width - 1; xp[0].y = 0; xp[1].x = rc.width - 1; xp[1].y = rc.height - 1; xp[2].x = 0; xp[2].y = rc.height - 1; XSetForeground(display, gc, darkGrey.pixel); XDrawLines(display, frame, gc, xp, 3, CoordModeOrigin); xp[0].x = rc.width - 3; xp[0].y = 1; xp[1].x = 1; xp[1].y = 1; xp[2].x = 1; xp[2].y = rc.height - 3; XSetForeground(display, gc, white.pixel); XDrawLines(display, frame, gc, xp, 3, CoordModeOrigin); xp[0].x = rc.width - 2; xp[0].y = 1; xp[1].x = rc.width - 2; xp[1].y = rc.height - 2; xp[2].x = 1; xp[2].y = rc.height - 2; XSetForeground(display, gc, darkGray.pixel); XDrawLines(display, frame, gc, xp, 3, CoordModeOrigin); /* * The frame of title area. */ xp[0].x = TITLE_WIDTH - 2; xp[0].y = 0; xp[1].x = 0; xp[1].y = 0; xp[2].x = 0; xp[2].y = TITLE_HEIGHT - 2; XSetForeground(display, gc, darkGray.pixel); XDrawLines(display, titleFrame, gc, xp, 3, CoordModeOrigin); xp[0].x = TITLE_WIDTH - 1; xp[0].y = 0; xp[1].x = TITLE_WIDTH - 1; xp[1].y = TITLE_HEIGHT - 1; xp[2].x = 0; xp[2].y = TITLE_HEIGHT - 1; XSetForeground(display, gc, white.pixel); XDrawLines(display, titleFrame, gc, xp, 3, CoordModeOrigin); xp[0].x = TITLE_WIDTH - 3; xp[0].y = 1; xp[1].x = 1; xp[1].y = 1; xp[2].x = 1; xp[2].y = TITLE_HEIGHT - 3; XSetForeground(display, gc, darkGrey.pixel); XDrawLines(display, titleFrame, gc, xp, 3, CoordModeOrigin); xp[0].x = TITLE_WIDTH - 2; xp[0].y = 1; xp[1].x = TITLE_WIDTH - 2; xp[1].y = TITLE_HEIGHT - 2; xp[2].x = 1; xp[2].y = TITLE_HEIGHT - 2; XSetForeground(display, gc, SwitcherColor.pixel); XDrawLines(display, titleFrame, gc, xp, 3, CoordModeOrigin); } /* * DrawTitle -- * Draw title name. */ void TaskSwitcher::DrawTitle(const char* name) { XRectangle ink, log; Point pt; XSetBackground(display, gc, SwitcherColor.pixel); XClearWindow(display, title); XmbTextExtents(fsTitle, name, strlen(name), &ink, &log); pt.x = 2 - log.x; pt.y = (TITLE_HEIGHT - log.height) / 2 - log.y; XSetForeground(display, gc, SwitcherStringColor.pixel); XmbDrawString(display, title, fsTitle, gc, pt.x, pt.y, name, strlen(name)); } /* * DrawRect -- * Draw the rectangle which indicates that the window is selected. */ void TaskSwitcher::DrawRect(int focusWin, int winNum) { int base; Rect rect(0, 0, 42, 42); if (winNum < 7) { base = (BASE_WIDTH - winNum * 43 - 3) / 2 + 3; if (direct) rect.x = base + ((focusWin - 1 + winNum) % winNum) * 43 + 1; else rect.x = base + ((focusWin + 1) % winNum) * 43 + 1; rect.y = 19 + 1; } else { base = 26; if (direct) { if (focusWin == 0) { rect.x = base + ((winNum - 1) % 7) * 43 + 1; rect.y = 19 + ((winNum - 1) / 7) * 43 + 1; } else { rect.x = base + ((focusWin + 6) % 7) * 43 + 1; rect.y = 19 + ((focusWin - 1) / 7) * 43 + 1; } } else { if (focusWin == winNum - 1) { rect.x = base + 1; rect.y = 19 + 1; } else { rect.x = base + ((focusWin + 1) % 7) * 43 + 1; rect.y = 19 + ((focusWin + 1) / 7) * 43 + 1; } } } /* * Erase the previous rectangle. */ XSetLineAttributes(display, gc, 2, LineSolid, CapButt, JoinMiter); if (SwitcherImage) { XClearArea(display, frame, rect.x - 1, rect.y - 1, rect.width + 1, 2, False); XClearArea(display, frame, rect.x - 1, rect.y - 1, 2, rect.height + 1, False); XClearArea(display, frame, rect.x + rect.width - 1, rect.y - 1, 2, rect.height + 1, False); XClearArea(display, frame, rect.x - 1, rect.y + rect.height - 1, rect.width + 1, 2, False); } else { XSetForeground(display, gc, SwitcherColor.pixel); XDrawRectangle(display, frame, gc, rect.x, rect.y, rect.width, rect.height); } /* * Draw new rectangle. */ rect.x = base + (focusWin % 7) * 43 + 1; rect.y = 19 + (focusWin / 7) * 43 + 1; XSetForeground(display, gc, SwitcherActiveColor.pixel); XDrawRectangle(display, frame, gc, rect.x, rect.y, rect.width, rect.height); /* * Restore line thickness. */ XSetLineAttributes(display, gc, 1, LineSolid, CapButt, JoinMiter); } /* * DrawPixmaps -- * Draw pixmaps which indicate windows. */ void TaskSwitcher::DrawPixmaps(int winNum) { int base = 26, num = 0; if (winNum < 7) base = (BASE_WIDTH - winNum * 43 - 3) / 2 + 3; // draw windows in the map list List::Iterator i(&focusMgr.GetMapList()); for (Blwm* blWm = i.GetHead(); blWm; blWm = i.GetNext()) { ASSERT(num < winNum); DrawPixmap(blWm, num, base); num++; } // draw windows in the unmap list List::Iterator j(&focusMgr.GetUnmapList()); for (Blwm* blWm = j.GetHead(); blWm; blWm = j.GetNext()) { ASSERT(num < winNum); DrawPixmap(blWm, num, base); num++; } } void TaskSwitcher::DrawPixmap(Blwm* blWm, int num, int base) { Point pt; pt.x = base + (num % 7) * 43 + 5; pt.y = 19 + (num / 7) * 43 + 5; BlImage* img = blWm->imgLarge; // use only here (not need Duplicate) img->Display(frame, pt); } void TaskSwitcher::Initialize() { swTimer = new Timer(); }