/*
* gnome.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/Xatom.h>
#include "main.h"
#include "gnome.h"
#include "paging.h"
#include "desktop.h"
#include "blwm.h"
#include "fbutton.h"
#include "list.h"
#include "blwmrc.h"
Atom Gnome::XA_WIN_SUPPORTING_WM_CHECK;
Atom Gnome::XA_WIN_PROTOCOLS;
Atom Gnome::XA_WIN_CLIENT_LIST;
Atom Gnome::XA_WIN_WORKSPACE_COUNT;
Atom Gnome::XA_WIN_WORKSPACE_NAMES;
Atom Gnome::XA_WIN_WORKSPACE;
Atom Gnome::XA_WIN_AREA_COUNT;
Atom Gnome::XA_WIN_AREA;
Atom Gnome::XA_WIN_STATE;
Atom Gnome::XA_WIN_HINTS;
Atom Gnome::XA_WIN_LAYER;
Atom Gnome::XA_WIN_EXPANDED_SIZE;
Atom Gnome::XA_WIN_DESKTOP_BUTTON_PROXY;
// called once at initialization
void Gnome::Init()
{
// for detection of a GNOME compliant window manager
XA_WIN_SUPPORTING_WM_CHECK = XInternAtom(display, "_WIN_SUPPORTING_WM_CHECK",
False);
// providing shortcuts managed clients
XA_WIN_CLIENT_LIST = XInternAtom(display, "_WIN_CLIENT_LIST", False);
// providing multiple/virtual desktop information
SetWorkspace();
// set active desktop
SetActiveDesktop();
InitClientProperties();
// listing GNOME window manager compliance
SetProtocols();
XA_WIN_DESKTOP_BUTTON_PROXY = XInternAtom(display,
"_WIN_DESKTOP_BUTTON_PROXY",
False);
}
void Gnome::SetProtocols()
{
Atom list[10];
XA_WIN_PROTOCOLS = XInternAtom(display, "_WIN_PROTOCOLS", False);
list[0] = XA_WIN_LAYER;
list[1] = XA_WIN_STATE;
list[2] = XA_WIN_HINTS;
list[3] = XInternAtom(display, "_WIN_APP_STATE", False);
list[4] = XA_WIN_EXPANDED_SIZE;
list[5] = XInternAtom(display, "_WIN_ICONS", False);
list[6] = XA_WIN_WORKSPACE;
list[7] = XA_WIN_WORKSPACE_COUNT;
list[8] = XA_WIN_WORKSPACE_NAMES;
list[9] = XA_WIN_CLIENT_LIST;
XChangeProperty(display, root, XA_WIN_PROTOCOLS, XA_ATOM, 32,
PropModeReplace, (unsigned char *)list, 10);
}
//
// Blwm uses WIN_AREA although it does not support multiple workspaces
// and Gnome recommends to use only WIN_WORKSPACE in this case.
// This is because WIN_WORKSPACE has no information on width and height
// of virtual desktop and gnome-pager's shape does not match blwm pager's.
//
void Gnome::SetWorkspace()
{
Rect rcVirt = paging->GetVirtRect();
int num, val[2];
// set the number of desktops for workspace
XA_WIN_WORKSPACE_COUNT = XInternAtom(display, "_WIN_WORKSPACE_COUNT", False);
num = 1; // fixed
XChangeProperty(display, root, XA_WIN_WORKSPACE_COUNT, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&num, 1);
// set the number of desktops for area
XA_WIN_AREA_COUNT = XInternAtom(display, "_WIN_AREA_COUNT", False);
val[0] = rcVirt.width;
val[1] = rcVirt.height;
XChangeProperty(display, root, XA_WIN_AREA_COUNT, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)val, 2);
// set desktop name
char* name = "Blwm Desktop";
XTextProperty text;
if (XStringListToTextProperty(&name, 1, &text)) {
XA_WIN_WORKSPACE_NAMES = XInternAtom(display, "_WIN_WORKSPACE_NAMES",
False);
XSetTextProperty(display, root, &text, XA_WIN_WORKSPACE_NAMES);
XFree(text.value);
}
// for active desktop
XA_WIN_WORKSPACE = XInternAtom(display, "_WIN_WORKSPACE", False);
XA_WIN_AREA = XInternAtom(display, "_WIN_AREA", False);
int index = 0; // fixed
XChangeProperty(display, root, XA_WIN_WORKSPACE, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&index, 1);
}
void Gnome::InitClientProperties()
{
XA_WIN_STATE = XInternAtom(display, "_WIN_STATE", False);
XA_WIN_HINTS = XInternAtom(display, "_WIN_HINTS", False);
XA_WIN_LAYER = XInternAtom(display, "_WIN_LAYER", False);
XA_WIN_EXPANDED_SIZE = XInternAtom(display, "_WIN_EXPANDED_SIZE", False);
}
// called when a new window is created
void Gnome::SetGnomeCompliant(Window win)
{
XChangeProperty(display, root, XA_WIN_SUPPORTING_WM_CHECK, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&win, 1);
XChangeProperty(display, win, XA_WIN_SUPPORTING_WM_CHECK, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&win, 1);
}
// called when a window is created and destroyed
void Gnome::ChangeClientList()
{
List<Blwm>* blwmList = &desktop.GetBlwmList();
List<Blwm>::Iterator i(blwmList);
Window* cliList;
Blwm* blWm;
int num = 0;
cliList = new Window[blwmList->GetSize()];
for (blWm = i.GetHead(); blWm; blWm = i.GetNext())
cliList[num++] = blWm->GetWin();
XChangeProperty(display, root, XA_WIN_CLIENT_LIST, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)cliList, num);
delete [] cliList;
}
// called when the desktop is changed
// If the desktop is not located according to the grid, the position is
// rounded off to the grid.
void Gnome::SetActiveDesktop()
{
Rect rcRoot = rootBlwm->GetRect();
Rect rcVirt = paging->GetVirtRect();
int val[2];
if (paging->origin.x >= 0)
val[0] = (paging->origin.x + rcRoot.width / 2) / rcRoot.width - rcVirt.x;
else
val[0] = (paging->origin.x - rcRoot.width / 2) / rcRoot.width - rcVirt.x;
if (paging->origin.y >= 0)
val[1] = (paging->origin.y + rcRoot.height / 2) / rcRoot.height - rcVirt.y;
else
val[1] = (paging->origin.y - rcRoot.height / 2) / rcRoot.height - rcVirt.y;
XChangeProperty(display, root, XA_WIN_AREA, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)val, 2);
}
// change the window property
void Gnome::SetProperty(Blwm* blWm, Atom atom, long* data)
{
if (atom == XA_WIN_STATE)
ProcessStateChange(blWm, data[0], data[1]);
else if (atom == XA_WIN_HINTS)
ProcessHintsChange(blWm, data[0], data[1]);
else if (atom == XA_WIN_LAYER)
ProcessLayerChange(blWm, data[0]);
else if (atom == XA_WIN_AREA) {
Rect rcVirt = paging->GetVirtRect();
Rect rcRoot = rootBlwm->GetRect();
Point pt;
pt.x = (data[0] + rcVirt.x) * rcRoot.width;
pt.y = (data[1] + rcVirt.y) * rcRoot.height;
paging->PagingProc(pt);
}
else if (atom == XA_WIN_EXPANDED_SIZE) {
#ifdef DEBUG
BlwmError("_WIN_EXPANDED_SIZE is currently not supported");
#endif
}
}
// mask stands for changed bits; state stands for a new state
void Gnome::ProcessStateChange(Blwm* blWm, long mask, long state)
{
if (mask & WIN_STATE_STICKY) {
if (state & WIN_STATE_STICKY) {
if (!blWm->CheckFlags(STICKY)) {
blWm->SetFlags(STICKY);
blWm->ConfigureNewRect(blWm->GetRect());
}
}
else {
if (blWm->CheckFlags(STICKY)) {
blWm->ResetFlags(STICKY);
blWm->ConfigureNewRect(blWm->GetRect());
}
}
}
if (mask & WIN_STATE_MINIMIZED) {
if (state & WIN_STATE_MINIMIZED) {
if (!blWm->CheckStatus(MINIMIZE_WINDOW))
blWm->MinimizeWindow(True, False);
}
else {
if (blWm->CheckStatus(MINIMIZE_WINDOW))
blWm->RestoreWindow(True, False);
}
}
if (mask & (WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED_HORIZ)) {
if (state & (WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED_HORIZ)) {
if (!blWm->CheckStatus(MAXIMIZE_WINDOW)) {
blWm->fButton[1]->ChangeImage(FrameButton::RESTORE);
blWm->MaximizeWindow(True, False);
rootBlwm->SetFocus();
blWm->SetFocus();
}
}
else {
if (blWm->CheckStatus(MAXIMIZE_WINDOW)) {
blWm->fButton[1]->ChangeImage(FrameButton::MAXIMIZE);
blWm->RestoreWindow(True, False);
rootBlwm->SetFocus();
blWm->SetFocus();
}
}
}
if (mask & WIN_STATE_HIDDEN) {
if (state & WIN_STATE_HIDDEN) {
if (!blWm->CheckFlags(NO_TBUTTON)) {
blWm->SetFlags(NO_TBUTTON);
blWm->tButton->UnmapButton();
if (UseTaskbar)
taskBar->RedrawAllTaskbarButtons();
}
}
}
}
// mask stands for changed bits; hints stands for a new hints
void Gnome::ProcessHintsChange(Blwm* blWm, long mask, long hints)
{
if (mask & WIN_HINTS_SKIP_FOCUS) {
if (hints & WIN_HINTS_SKIP_FOCUS)
blWm->SetFlags(SKIP_FOCUS); // XXX currently not effect
else
blWm->ResetFlags(SKIP_FOCUS);
}
if (mask & WIN_HINTS_SKIP_TASKBAR) { // same as WIN_STATE_HIDDEN?
if (hints & WIN_HINTS_SKIP_TASKBAR) {
if (!blWm->CheckFlags(NO_TBUTTON)) {
blWm->SetFlags(NO_TBUTTON);
blWm->tButton->UnmapButton();
if (UseTaskbar)
taskBar->RedrawAllTaskbarButtons();
}
}
}
if (mask & WIN_HINTS_FOCUS_ON_CLICK) {
if (hints & WIN_HINTS_FOCUS_ON_CLICK)
blWm->SetFlags(FOCUS_ON_CLICK);
}
// XXX currently ignored
if ((hints & WIN_HINTS_SKIP_WINLIST) ||
(hints & WIN_HINTS_GROUP_TRANSIENT)) {
#ifdef DEBUG
BlwmError("_WIN_HINTS %d is currently not supported", hints);
#endif
}
}
void Gnome::ProcessLayerChange(Blwm* blWm, long layer)
{
switch (layer) {
case WIN_LAYER_DESKTOP:
case WIN_LAYER_BELOW:
if (blWm->CheckFlags(ONTOP)) {
blWm->ResetFlags(ONTOP);
desktop.GetOnTopList().Remove(blWm);
}
blWm->LowerWindow();
break;
case WIN_LAYER_NORMAL:
if (blWm->CheckFlags(ONTOP)) {
blWm->ResetFlags(ONTOP);
desktop.GetOnTopList().Remove(blWm);
}
break;
case WIN_LAYER_ONTOP:
if (!blWm->CheckFlags(ONTOP)) {
blWm->SetFlags(ONTOP);
desktop.GetOnTopList().InsertTail(blWm);
blWm->RaiseWindow(True);
}
break;
default:
#ifdef DEBUG
BlwmError("_WIN_LAYER %d is currently not supported", layer);
#endif
break;
}
}
Bool Gnome::GetAtomValue(Blwm* blWm, Atom atom, long* val)
{
Atom atype;
int aformat;
unsigned long nitems, bytes_remain;
unsigned char *prop = NULL;
if (XGetWindowProperty(display, blWm->GetWin(), atom, 0, 1, False, atom,
&atype, &aformat, &nitems, &bytes_remain, &prop)
!= Success)
return False;
if (prop) {
if (nitems == 1)
*val = *(long *)∝
XFree(prop);
}
return (nitems == 1);
}
void Gnome::SetState(Blwm* blWm, long state)
{
long val;
if (GetAtomValue(blWm, XA_WIN_STATE, &val))
val |= state;
else
val = state;
XChangeProperty(display, blWm->GetWin(), XA_WIN_STATE, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&val, 1);
}
void Gnome::ResetState(Blwm* blWm, long state)
{
long val;
if (!GetAtomValue(blWm, XA_WIN_STATE, &val))
return;
val &= ~state;
XChangeProperty(display, blWm->GetWin(), XA_WIN_STATE, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&val, 1);
}
void Gnome::SetInitStates(Blwm* blWm)
{
long state = 0;
if (blWm->CheckFlags(STICKY))
state |= WIN_STATE_STICKY;
if (blWm->CheckFlags(INIT_MAXIMIZE))
state |= WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED_HORIZ;
if (blWm->CheckFlags(INIT_MINIMIZE))
state |= WIN_STATE_MINIMIZED;
if (blWm->CheckFlags(NO_TBUTTON))
state |= WIN_STATE_HIDDEN;
if (state)
SetState(blWm, state);
}
void Gnome::ChangeLayer(Blwm* blWm, long layer)
{
XChangeProperty(display, blWm->GetWin(), XA_WIN_LAYER, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&layer, 1);
}
// check state, hints, and layer at creating a window
void Gnome::GetHints(Blwm* blWm)
{
// check state
long state;
if (GetAtomValue(blWm, XA_WIN_STATE, &state)) {
if (state & WIN_STATE_STICKY)
blWm->SetFlags(STICKY);
if (state & WIN_STATE_MINIMIZED)
blWm->SetFlags(INIT_MINIMIZE);
if (state & (WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED_HORIZ))
blWm->SetFlags(INIT_MAXIMIZE);
if (state & WIN_STATE_HIDDEN)
blWm->SetFlags(NO_TBUTTON);
if (state & WIN_STATE_FIXED_POSITION)
blWm->SetFlags(FIXED_POS);
}
// check hints
long hints;
if (GetAtomValue(blWm, XA_WIN_HINTS, &hints)) {
if (hints & WIN_HINTS_SKIP_FOCUS)
blWm->SetFlags(SKIP_FOCUS); // XXX currently not effect
if (hints & WIN_HINTS_SKIP_TASKBAR)
blWm->SetFlags(NO_TBUTTON);
if (hints & WIN_HINTS_FOCUS_ON_CLICK)
blWm->SetFlags(FOCUS_ON_CLICK);
}
// check layer
long layer;
if (GetAtomValue(blWm, XA_WIN_LAYER, &layer)) {
if (layer == WIN_LAYER_ONTOP)
blWm->SetFlags(ONTOP);
}
}
// XXX currently not proxy any desktop button events
void Gnome::ProxyDesktopEvent(XEvent* ev)
{
Window win;
if (ev->type == ButtonPress)
XUngrabPointer(display, CurrentTime);
if (!GetAtomValue(rootBlwm, XA_WIN_DESKTOP_BUTTON_PROXY, (long *)&win))
return;
if (win != None)
XSendEvent(display, win, False, SubstructureNotifyMask, ev);
}
syntax highlighted by Code2HTML, v. 0.9.1