/*
* 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 <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#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<Blwm>::Iterator(&focusMgr.GetMapList());
iterUnmap = new List<Blwm>::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<Blwm>::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<Blwm>::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();
}
syntax highlighted by Code2HTML, v. 0.9.1