/*
* gnome.cc
*
* Copyright (C) 1995-2001 Kenichi Kourai
*
* 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, 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 qvwm; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 "qvwm.h"
#include "fbutton.h"
#include "list.h"
#include "qvwmrc.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);
}
//
// Qvwm 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 qvwm 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 = "Qvwm 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<Qvwm>* qvwmList = &desktop.GetQvwmList();
List<Qvwm>::Iterator i(qvwmList);
Window* cliList;
Qvwm* qvWm;
int num = 0;
cliList = new Window[qvwmList->GetSize()];
for (qvWm = i.GetHead(); qvWm; qvWm = i.GetNext())
cliList[num++] = qvWm->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 = rootQvwm->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(Qvwm* qvWm, Atom atom, long* data)
{
if (atom == XA_WIN_STATE)
ProcessStateChange(qvWm, data[0], data[1]);
else if (atom == XA_WIN_HINTS)
ProcessHintsChange(qvWm, data[0], data[1]);
else if (atom == XA_WIN_LAYER)
ProcessLayerChange(qvWm, data[0]);
else if (atom == XA_WIN_AREA) {
Rect rcVirt = paging->GetVirtRect();
Rect rcRoot = rootQvwm->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
QvwmError("_WIN_EXPANDED_SIZE is currently not supported");
#endif
}
}
// mask stands for changed bits; state stands for a new state
void Gnome::ProcessStateChange(Qvwm* qvWm, long mask, long state)
{
if (mask & WIN_STATE_STICKY) {
if (state & WIN_STATE_STICKY) {
if (!qvWm->CheckFlags(STICKY)) {
qvWm->SetFlags(STICKY);
qvWm->ConfigureNewRect(qvWm->GetRect());
}
}
else {
if (qvWm->CheckFlags(STICKY)) {
qvWm->ResetFlags(STICKY);
qvWm->ConfigureNewRect(qvWm->GetRect());
}
}
}
if (mask & WIN_STATE_MINIMIZED) {
if (state & WIN_STATE_MINIMIZED) {
if (!qvWm->CheckStatus(MINIMIZE_WINDOW))
qvWm->MinimizeWindow(True, False);
}
else {
if (qvWm->CheckStatus(MINIMIZE_WINDOW))
qvWm->RestoreWindow(True, False);
}
}
if (mask & (WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED_HORIZ)) {
if (state & (WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED_HORIZ)) {
if (!qvWm->CheckStatus(MAXIMIZE_WINDOW)) {
qvWm->fButton[1]->ChangeImage(FrameButton::RESTORE);
qvWm->MaximizeWindow(True, False);
rootQvwm->SetFocus();
qvWm->SetFocus();
}
}
else {
if (qvWm->CheckStatus(MAXIMIZE_WINDOW)) {
qvWm->fButton[1]->ChangeImage(FrameButton::MAXIMIZE);
qvWm->RestoreWindow(True, False);
rootQvwm->SetFocus();
qvWm->SetFocus();
}
}
}
if (mask & WIN_STATE_HIDDEN) {
if (state & WIN_STATE_HIDDEN) {
if (!qvWm->CheckFlags(NO_TBUTTON)) {
qvWm->SetFlags(NO_TBUTTON);
qvWm->tButton->UnmapButton();
if (UseTaskbar)
taskBar->RedrawAllTaskbarButtons();
}
}
}
}
// mask stands for changed bits; hints stands for a new hints
void Gnome::ProcessHintsChange(Qvwm* qvWm, long mask, long hints)
{
if (mask & WIN_HINTS_SKIP_FOCUS) {
if (hints & WIN_HINTS_SKIP_FOCUS)
qvWm->SetFlags(SKIP_FOCUS); // XXX currently not effect
else
qvWm->ResetFlags(SKIP_FOCUS);
}
if (mask & WIN_HINTS_SKIP_TASKBAR) { // same as WIN_STATE_HIDDEN?
if (hints & WIN_HINTS_SKIP_TASKBAR) {
if (!qvWm->CheckFlags(NO_TBUTTON)) {
qvWm->SetFlags(NO_TBUTTON);
qvWm->tButton->UnmapButton();
if (UseTaskbar)
taskBar->RedrawAllTaskbarButtons();
}
}
}
if (mask & WIN_HINTS_FOCUS_ON_CLICK) {
if (hints & WIN_HINTS_FOCUS_ON_CLICK)
qvWm->SetFlags(FOCUS_ON_CLICK);
}
// XXX currently ignored
if ((hints & WIN_HINTS_SKIP_WINLIST) ||
(hints & WIN_HINTS_GROUP_TRANSIENT)) {
#ifdef DEBUG
QvwmError("_WIN_HINTS %d is currently not supported", hints);
#endif
}
}
void Gnome::ProcessLayerChange(Qvwm* qvWm, long layer)
{
switch (layer) {
case WIN_LAYER_DESKTOP:
case WIN_LAYER_BELOW:
if (qvWm->CheckFlags(ONTOP)) {
qvWm->ResetFlags(ONTOP);
desktop.GetOnTopList().Remove(qvWm);
}
qvWm->LowerWindow();
break;
case WIN_LAYER_NORMAL:
if (qvWm->CheckFlags(ONTOP)) {
qvWm->ResetFlags(ONTOP);
desktop.GetOnTopList().Remove(qvWm);
}
break;
case WIN_LAYER_ONTOP:
if (!qvWm->CheckFlags(ONTOP)) {
qvWm->SetFlags(ONTOP);
desktop.GetOnTopList().InsertTail(qvWm);
qvWm->RaiseWindow(True);
}
break;
default:
#ifdef DEBUG
QvwmError("_WIN_LAYER %d is currently not supported", layer);
#endif
break;
}
}
Bool Gnome::GetAtomValue(Qvwm* qvWm, Atom atom, long* val)
{
Atom atype;
int aformat;
unsigned long nitems, bytes_remain;
unsigned char *prop = NULL;
if (XGetWindowProperty(display, qvWm->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(Qvwm* qvWm, long state)
{
long val;
if (GetAtomValue(qvWm, XA_WIN_STATE, &val))
val |= state;
else
val = state;
XChangeProperty(display, qvWm->GetWin(), XA_WIN_STATE, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&val, 1);
}
void Gnome::ResetState(Qvwm* qvWm, long state)
{
long val;
if (!GetAtomValue(qvWm, XA_WIN_STATE, &val))
return;
val &= ~state;
XChangeProperty(display, qvWm->GetWin(), XA_WIN_STATE, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&val, 1);
}
void Gnome::SetInitStates(Qvwm* qvWm)
{
long state = 0;
if (qvWm->CheckFlags(STICKY))
state |= WIN_STATE_STICKY;
if (qvWm->CheckFlags(INIT_MAXIMIZE))
state |= WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED_HORIZ;
if (qvWm->CheckFlags(INIT_MINIMIZE))
state |= WIN_STATE_MINIMIZED;
if (qvWm->CheckFlags(NO_TBUTTON))
state |= WIN_STATE_HIDDEN;
if (state)
SetState(qvWm, state);
}
void Gnome::ChangeLayer(Qvwm* qvWm, long layer)
{
XChangeProperty(display, qvWm->GetWin(), XA_WIN_LAYER, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&layer, 1);
}
// check state, hints, and layer at creating a window
void Gnome::GetHints(Qvwm* qvWm)
{
// check state
long state;
if (GetAtomValue(qvWm, XA_WIN_STATE, &state)) {
if (state & WIN_STATE_STICKY)
qvWm->SetFlags(STICKY);
if (state & WIN_STATE_MINIMIZED)
qvWm->SetFlags(INIT_MINIMIZE);
if (state & (WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED_HORIZ))
qvWm->SetFlags(INIT_MAXIMIZE);
if (state & WIN_STATE_HIDDEN)
qvWm->SetFlags(NO_TBUTTON);
if (state & WIN_STATE_FIXED_POSITION)
qvWm->SetFlags(FIXED_POS);
}
// check hints
long hints;
if (GetAtomValue(qvWm, XA_WIN_HINTS, &hints)) {
if (hints & WIN_HINTS_SKIP_FOCUS)
qvWm->SetFlags(SKIP_FOCUS); // XXX currently not effect
if (hints & WIN_HINTS_SKIP_TASKBAR)
qvWm->SetFlags(NO_TBUTTON);
if (hints & WIN_HINTS_FOCUS_ON_CLICK)
qvWm->SetFlags(FOCUS_ON_CLICK);
}
// check layer
long layer;
if (GetAtomValue(qvWm, XA_WIN_LAYER, &layer)) {
if (layer == WIN_LAYER_ONTOP)
qvWm->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(rootQvwm, 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