/*
* blwm.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 <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>
#include "main.h"
#include "misc.h"
#include "blwm.h"
#include "util.h"
#include "fbutton.h"
#include "tbutton.h"
#include "sbutton.h"
#include "menu.h"
#include "startmenu.h"
#include "event.h"
#include "taskbar.h"
#include "icon.h"
#include "blwmrc.h"
#include "switcher.h"
#include "paging.h"
#include "pager.h"
#include "mini.h"
#include "key.h"
#include "indicator.h"
#include "focus_mgr.h"
#include "desktop.h"
#include "mwm.h"
#include "gnome.h"
#include "exec.h"
#include "image.h"
#include "session.h"
#include "tooltip.h"
Bool Blwm::umReserved = False;
XContext Blwm::context;
int Blwm::TITLE_HEIGHT;
int Blwm::BORDER_WIDTH;
int Blwm::TOP_BORDER;
/*
* Blwm class constructor --
* Position the window at that point if usrPos is True;
* otherwise, the position of the window obeys a given rule.
*/
Blwm::Blwm(Window w, Bool usrPos)
: wOrig(w)
{
flags = 0;
status = 0;
mapped = False;
imgLarge = NULL;
imgSmall = NULL;
nCmapWins = 0;
name = shortname = NULL;
isFirstMapped = True;
#ifdef USE_XSMP
smClientId = NULL;
wmRole = NULL;
#endif
XGrabServer(display);
/*
* Get some hints.
*/
wmHints = XGetWMHints(display, w);
GetWindowSizeHints();
Gnome::GetHints(this);
Mwm::GetHints(this);
GetWindowClassHints();
FetchWMProtocols();
GetTransientForHint();
Gnome::SetInitStates(this);
GetWindowAttrs(usrPos);
if (Intersect(rc, rootBlwm->GetRect()))
Gnome::ResetState(this, WIN_STATE_HID_WORKSPACE);
else
Gnome::SetState(this, WIN_STATE_HID_WORKSPACE);
Rect rcCtrl, rcButton[3];
CalcFramePos(rc, &rcParent, &rcTitle, rcCorner, rcSide, rcEdge, &rcCtrl,
rcButton);
CreateFrame(rc);
CreateParent(rcParent);
CreateSides(rcSide);
CreateCorners(rcCorner);
CreateEdges(rcEdge);
CreateTitle(rcTitle);
CreateCtrlButton(rcCtrl);
if (CheckFlags(THIN_BORDER))
XSetWindowBorderWidth(display, frame, 1);
/*
* Create three kinds of frame button.
*/
fButton[0] = new FrameButton1(this, title, rcButton[0]);
fButton[0]->ChangeImage(FrameButton::MINIMIZE);
fButton[1] = new FrameButton2(this, title, rcButton[1]);
fButton[1]->ChangeImage(FrameButton::MAXIMIZE);
fButton[2] = new FrameButton3(this, title, rcButton[2]);
fButton[2]->ChangeImage(FrameButton::CLOSE);
if (imgLarge == NULL)
imgLarge = GetFixedIcon(32);
if (imgSmall == NULL)
imgSmall = GetFixedIcon(16);
Rect rect(-50, -50, 50, 50);
BlImage* img = imgSmall->Duplicate();
tButton = new TaskbarButton(this, rect, img);
ctrlMenu = ::ctrlMenu;
ChangeOrigWindow();
SendConfigureEvent();
int bShaped, cShaped;
int xbs, ybs, xcs, ycs;
unsigned int wbs, hbs, wcs, hcs;
if (shapeSupport) {
XShapeSelectInput(display, w, ShapeNotifyMask);
XShapeQueryExtents(display, w, &bShaped, &xbs, &ybs, &wbs, &hbs,
&cShaped, &xcs, &ycs, &wcs, &hcs);
if (bShaped) {
SetFlags(SHAPED);
SetShape();
}
}
if (UsePager)
mini = new Miniature(this, rc);
scKey->GrabKeys(frame);
SetTransient();
XUngrabServer(display);
}
/*
* Blwm class constructor --
* For only root window.
*/
Blwm::Blwm(Window root)
: wOrig(root), frame(root)
{
Rect rect(0, 0, BUTTON_WIDTH, BUTTON_HEIGHT);
rc = Rect(0, 0,
DisplayWidth(display, screen), DisplayHeight(display, screen));
parent = title = ctrl = None;
for (int i = 0; i < 4; i++) {
corner[i] = None;
side[i]= None;
}
mapped = True;
flags = 0L;
status = 0;
nCmapWins = 0;
name = new char[10];
strcpy(name, "blwm root");
shortname = NULL;
toolTip = NULL;
#ifdef USE_XSMP
smClientId = NULL;
wmRole = NULL;
#endif
XRectangle ink, log;
XmbTextExtents(fsTaskbar, StartButtonTitle, strlen(StartButtonTitle),
&ink, &log);
log.width += 2 + StartButton::SYMBOL_SIZE + 4 + 2 + 4;
taskBar = new Taskbar(this, log.width + 4, TaskbarRows);
tButton = new StartButton(this, log.width);
rect = Rect(0, 0, 0, 0);
ctrlMenu = new Menu(DesktopMenuItem, fsCtrlMenu, NULL, this);
XSaveContext(display, root, context, (caddr_t)this);
focusBlwm = this;
scKey->GrabKeys(root);
}
Blwm::~Blwm()
{
int i;
if (this != rootBlwm) {
XDeleteContext(display, frame, context);
XDeleteContext(display, parent, context);
XDeleteContext(display, wOrig, context);
XDeleteContext(display, title, context);
for (i = 0; i < 4; i++) {
XDeleteContext(display, corner[i], context);
XDeleteContext(display, side[i], context);
}
XDeleteContext(display, ctrl, context);
BlImage::Destroy(imgLarge);
BlImage::Destroy(imgSmall);
if (TitlebarImage)
BlImage::Destroy(imgTitle);
if (TitlebarActiveImage)
BlImage::Destroy(imgActiveTitle);
if (FrameImage) {
for (i = 0; i < 4; i++) {
BlImage::Destroy(imgSide[i]);
BlImage::Destroy(imgCorner[i]);
}
}
if (FrameActiveImage) {
for (i = 0; i < 4; i++) {
BlImage::Destroy(imgActiveSide[i]);
BlImage::Destroy(imgActiveCorner[i]);
}
}
if (GradTitlebar) {
if (pixGrad)
XFreePixmap(display, pixGrad);
if (pixActiveGrad)
XFreePixmap(display, pixActiveGrad);
}
delete toolTip;
if (wmHints)
XFree(wmHints);
if (classHints.res_name)
XFree(classHints.res_name);
if (classHints.res_class)
XFree(classHints.res_class);
#ifdef USE_XSMP
if (wmRole)
XFree(wmRole);
if (smClientId)
XFree(smClientId);
#endif
/*
* Delete frame buttons before the parent window(frame) is destroyed.
*/
for (i = 0; i < 3; i++)
delete fButton[i];
delete tButton;
/*
* When this is a transient window.
*/
if (CheckFlags(TRANSIENT) && blMain) {
List<Blwm>::Iterator li(&blMain->trList);
Blwm* blWm = li.GetHead();
while (blWm) {
if (blWm == this) {
blMain->trList.Remove(this);
break;
}
blWm = li.GetNext();
}
}
else {
/*
* When this is a main window which has some transient windows.
*/
List<Blwm>::Iterator li(&trList);
Blwm* blTrans = li.GetHead();
while (blTrans) {
blTrans->blMain = NULL;
blTrans = li.GetNext();
}
}
if (CheckFlags(ONTOP))
desktop.GetOnTopList().Remove(this);
if (UsePager)
delete mini;
XDestroyWindow(display, frame);
}
else
delete ctrlMenu;
delete [] name;
delete [] shortname;
}
void Blwm::GetWindowAttrs(Bool usrPos)
{
XWindowAttributes attr;
XGetWindowAttributes(display, wOrig, &attr);
/*
* Preserve original border width and set border width to 0.
*/
bwOrig = attr.border_width;
XSetWindowBorderWidth(display, wOrig, 0);
#ifdef USE_XSMP
/*
* Restore properties and attributes from the previous session
*/
GetSMClientId(); // initialize smClientId
GetWMRole(); // initialize wmRole
int ax, ay, aw, ah;
if (session->SearchProto(smClientId, wmRole,
classHints.res_class,
classHints.res_name,
name, &ax, &ay, &aw, &ah)) {
usrPos = True; // force place
attr.x = ax;
attr.y = ay;
attr.width = aw;
attr.height = ah;
}
#endif // USE_XSMP
Point pageoff;
ExecPending* pending = ExecPending::LookInList(name, classHints);
if (pending) {
pageoff = pending->GetPageOff();
ExecPending::m_pendingList.Remove(pending);
delete pending;
}
else
pageoff = paging->origin;
/*
* Calculate the window position.
*/
if (usrPos || (flags & (TRANSIENT | FIXED_POS)) ||
(hints.flags & (USPosition | PPosition))) {
if (CheckFlags(GEOMETRY)) {
if (geom->bitmask & WidthValue)
attr.width = geom->rc.width;
if (geom->bitmask & HeightValue)
attr.height = geom->rc.height;
}
if (CheckFlags(STICKY))
rcOrig = Rect(attr.x, attr.y, attr.width, attr.height);
else
rcOrig = Rect(attr.x + pageoff.x, attr.y + pageoff.y,
attr.width, attr.height);
CalcWindowPos(rcOrig);
}
else {
if (CheckFlags(GEOMETRY)) {
if (geom->bitmask & WidthValue)
attr.width = geom->rc.width;
if (geom->bitmask & HeightValue)
attr.height = geom->rc.height;
if (geom->bitmask & XNegative)
attr.x = DisplayWidth(display, screen) + geom->rc.x - attr.width;
else if (geom->bitmask & XValue)
attr.x = geom->rc.x;
if (geom->bitmask & YNegative)
attr.y = DisplayHeight(display, screen) + geom->rc.y - attr.height;
else if (geom->bitmask & YValue)
attr.y = geom->rc.y;
if (CheckFlags(STICKY))
rcOrig = Rect(attr.x, attr.y, attr.width, attr.height);
else
rcOrig = Rect(attr.x + pageoff.x, attr.y + pageoff.y,
attr.width, attr.height);
CalcWindowPos(rcOrig);
}
else {
int borderWidth, topBorder, titleHeight, titleEdge;
GetBorderAndTitle(borderWidth, topBorder, titleHeight, titleEdge);
rc.width = attr.width + borderWidth * 2;
rc.height = attr.height + borderWidth + topBorder + titleHeight
+ titleEdge;
Point pt = desktop.GetNextPlace(Dim(rc.width, rc.height), pageoff);
rc.x = pt.x;
rc.y = pt.y;
if (CheckFlags(STICKY))
rcOrig = Rect(rc.x - rcScreen.x - pageoff.x,
rc.y - rcScreen.y - pageoff.y,
attr.width,
attr.height);
else
rcOrig = Rect(rc.x - rcScreen.x, rc.y - rcScreen.y,
attr.width, attr.height);
}
}
}
/*
* ChangeOrigWindow --
* Change parent and attributes of original window.
*/
void Blwm::ChangeOrigWindow()
{
XSetWindowAttributes attributes;
unsigned long valueMask;
XReparentWindow(display, wOrig, parent, 0, 0);
attributes.event_mask = StructureNotifyMask | PropertyChangeMask |
EnterWindowMask | LeaveWindowMask | ColormapChangeMask;
attributes.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
valueMask = CWEventMask | CWDontPropagate;
XChangeWindowAttributes(display, wOrig, valueMask, &attributes);
XSaveContext(display, wOrig, context, (caddr_t)this);
XAddToSaveSet(display, wOrig);
}
/*
* SetTransient
* Search pre-mapped transient windows for this window to handle
* a transient window properly even if the main window is mapped
* after the transient window is mapped first.
*/
void Blwm::SetTransient()
{
List<Blwm>::Iterator i(&desktop.GetBlwmList());
Window tWin;
for (Blwm* blWm = i.GetHead(); blWm; blWm = i.GetNext()) {
if (blWm->CheckFlags(TRANSIENT) && blWm->blMain == NULL)
if (XGetTransientForHint(display, blWm->GetWin(), &tWin))
if (tWin == wOrig) {
blWm->blMain = this; // blWm is a transient window of this
if (CheckFlags(ONTOP) && !blWm->CheckFlags(ONTOP)) {
blWm->SetFlags(ONTOP);
desktop.GetOnTopList().InsertTail(blWm);
Gnome::ChangeLayer(blWm, WIN_LAYER_ONTOP);
}
trList.InsertTail(blWm);
}
}
}
/*
* SendMessage --
* Send the messeage to this window.
*/
void Blwm::SendMessage(Atom atom)
{
XClientMessageEvent cev;
cev.type = ClientMessage;
cev.window = wOrig;
cev.message_type = _XA_WM_PROTOCOLS;
cev.format = 32;
cev.data.l[0] = atom;
cev.data.l[1] = CurrentTime;
#ifdef DEBUG
if (atom == _XA_WM_DELETE_WINDOW)
printf("Send delete msg to '%s'\n", name);
#endif
XSendEvent(display, wOrig, False, 0L, (XEvent *)&cev);
}
/*
* AdjustPage --
* Switch to the page where the window center point exists.
*/
void Blwm::AdjustPage()
{
if (this == rootBlwm)
return;
if (CheckFlags(STICKY))
return;
Rect rect = GetRect();
Point ptCenter(rect.x+rect.width/2, rect.y+rect.height/2);
Rect rcRoot = rootBlwm->GetRect();
if (ptCenter.x >= 0)
ptCenter.x = ptCenter.x / rcRoot.width * rcRoot.width;
else
ptCenter.x = (ptCenter.x / rcRoot.width - 1) * rcRoot.width;
if (ptCenter.y >= 0)
ptCenter.y = ptCenter.y / rcRoot.height * rcRoot.height;
else
ptCenter.y = (ptCenter.y / rcRoot.height - 1) * rcRoot.height;
ASSERT(paging);
paging->PagingProc(ptCenter, False);
}
/*
* Exposure --
* Process exposure event.
*/
void Blwm::Exposure(Window win)
{
if (win == ctrl)
DrawCtrlMenuMark();
else if (win == title)
DrawTitle(False);
else
DrawFrame(win); // win may not be a part of frame windows
}
/*
* Button1Press --
* Process the press of button1(mouse left button).
*/
void Blwm::Button1Press(Window win, Time clickTime, const Point& ptRoot,
unsigned int state)
{
if (this != rootBlwm)
toolTip->Disable();
if (UseTaskbar && TaskbarAutoHide && !taskBar->IsHiding())
taskBar->HideTaskbar();
if (win == ctrl)
CtrlButton1Press(clickTime, ptRoot, state);
else {
Menu::UnmapAllMenus();
if (!CheckFocus())
SetFocus();
if (Icon::focusIcon != NULL)
Icon::focusIcon->ResetFocus();
if (ClickingRaises || win != wOrig)
RaiseWindow(True);
/*
* Title bar.
*/
if (win == title)
TitleButton1Press(clickTime, ptRoot);
SetStatus(PRESS_FRAME);
}
if (AutoRaise && focusBlwm == this && activeBlwm != this)
RaiseWindow(True);
}
/*
* Button1Release --
* Process the release of button1(mouse left button).
*/
void Blwm::Button1Release()
{
ResetStatus(PRESS_FRAME);
/*
* Unmap control menu if the menu is shown and menu button is pressed.
*/
if (umReserved) {
ASSERT(ctrlMenu);
ctrlMenu->UnmapMenu();
umReserved = False;
}
}
void Blwm::Button2Press(Window win)
{
if (this != rootBlwm)
toolTip->Disable();
if (win == side[0] || win == side[1] || win == side[2] || win == side[3] ||
win == corner[0] || win == corner[1] || win == corner[2] ||
win == corner[3])
SetStatus(PRESS_FRAME);
if (AutoRaise && focusBlwm == this && activeBlwm != this)
RaiseWindow(True);
}
void Blwm::Button2Release(Window win)
{
ResetStatus(PRESS_FRAME);
if (win != frame)
LowerWindow();
}
void Blwm::Button3Press()
{
if (this != rootBlwm)
toolTip->Disable();
if (AutoRaise && focusBlwm == this && activeBlwm != this)
RaiseWindow(True);
}
/*
* Button3Release --
* Process the release of button3(mouse right button).
*/
void Blwm::Button3Release(Window win, const Point& ptRoot)
{
Point pt;
int dir;
if (this == rootBlwm) {
Menu::UnmapAllMenus();
if (!CheckFocus())
SetFocus();
ASSERT(ctrlMenu);
pt = ctrlMenu->GetFixedMenuPos(ptRoot, dir);
ctrlMenu->MapMenu(pt.x, pt.y, dir);
}
else {
Menu::UnmapAllMenus();
if (!CheckFocus())
SetFocus();
if (CheckMapped())
RaiseWindow(True);
else
RestoreWindow();
ASSERT(ctrlMenu);
ctrlMenu->SetBlwm(this);
pt = ctrlMenu->GetFixedMenuPos(ptRoot, dir);
ctrlMenu->MapMenu(pt.x, pt.y, dir);
}
}
/*
* Button1Motion --
* Move or resize according to mouse movement.
*/
void Blwm::Button1Motion(Window win, const Point& ptRoot)
{
if (win == title && !CheckStatus(MAXIMIZE_WINDOW))
MoveWindow(ptRoot, True);
else if (win == side[0])
ResizeWindow(P_LEFT, True);
else if (win == side[1])
ResizeWindow(P_RIGHT, True);
else if (win == side[2])
ResizeWindow(P_TOP, True);
else if (win == side[3])
ResizeWindow(P_BOTTOM, True);
else if (win == corner[0])
ResizeWindow(P_LEFT | P_TOP, True);
else if (win == corner[1])
ResizeWindow(P_RIGHT | P_TOP, True);
else if (win == corner[2])
ResizeWindow(P_LEFT | P_BOTTOM, True);
else if (win == corner[3])
ResizeWindow(P_RIGHT | P_BOTTOM, True);
}
void Blwm::Button2Motion(Window win, const Point& ptRoot)
{
if (!CheckStatus(MAXIMIZE_WINDOW))
MoveWindow(ptRoot, True);
}
void Blwm::Enter(Window win, unsigned int state)
{
if (win == title) {
if (strcmp(name, shortname) != 0)
toolTip->SetTimer();
}
if ((state & NoFocusChangeMask) == NoFocusChangeMask)
return;
if (!ClickToFocus && !(NoDesktopFocus && this == rootBlwm) &&
!CheckStatus(MINIMIZE_WINDOW) && !CheckFlags(FOCUS_ON_CLICK)) {
if (!CheckFocus() && !Menu::CheckAnyMenusMapped()) {
if (AutoRaise)
RaiseWindow(False);
SetFocus();
}
}
}
void Blwm::Leave(Window win)
{
if (win == title)
toolTip->Disable();
}
void Blwm::PointerMotion(Window win)
{
if (win == title) {
if (!toolTip->IsMapped())
toolTip->ResetTimer();
}
}
void Blwm::Initialize()
{
context = XUniqueContext();
TITLE_HEIGHT = FrameTitleHeight;
BORDER_WIDTH = FrameBorderWidth + 4;
TOP_BORDER = FrameBorderWidth + 2;
CreateTitlebarPixmap();
CreateFramePixmap();
}
#ifdef USE_XSMP
/*
* SavePropertiesToFile --
* Save the state of this window to the FILE for XSMP
*/
Bool Blwm::SavePropertiesToFile(int idx, FILE* fp)
{
if (blMain) {
// this window is a transient win.
return True;
}
GetSMClientId();
GetWMRole();
// WM_CLASS (if WM_WINDOW_ROLE is not specified)
XClassHint wm_class; // STRING
if (wmRole == NULL) {
if (XGetClassHint(display, wOrig, &wm_class) == 0)
return False;
}
// Write out to the 'fp'
if (smClientId != NULL)
fprintf(fp, "%d,id=%s\n", idx, smClientId); // SM_CLIENT_ID
fprintf(fp, "%d,wOrig=%x\n", idx, (int)wOrig);
fprintf(fp, "%d,frame=%x\n", idx, (int)frame);
if (wmRole)
fprintf(fp, "%d,role=%s\n", idx, wmRole); // WM_WINDOW_ROLE
else {
fprintf(fp, "%d,class=%s\n", idx, wm_class.res_class);// WM_CLASS
fprintf(fp, "%d,rname=%s\n", idx, wm_class.res_name);
fprintf(fp, "%d,name=%s\n", idx, name); // WM_NAME
}
fprintf(fp, "%d,pos=%d,%d\n", idx, rcOrig.x, rcOrig.y);
fprintf(fp, "%d,size=%d,%d\n", idx, rcOrig.width, rcOrig.height);
if (wmRole == NULL) {
XFree(wm_class.res_class);
XFree(wm_class.res_name);
}
return True;
}
#endif // USE_XSMP
syntax highlighted by Code2HTML, v. 0.9.1