/* * 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 #include #include #include #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* blwmList = &desktop.GetBlwmList(); List::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); }