Logo Search packages:      
Sourcecode: bbkeys version File versions  Download package

window.cc

// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
// window.cc for Epistrophy - a key handler for NETWM/EWMH window managers.
// Copyright (c) 2002 - 2002 Ben Jansens <ben at orodu.net>
//
// Modified for use and inclusion in bbkeys by Jason 'vanRijn' Kasper
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#include <iostream>

using std::cout;
using std::endl;

#include "window.hh"

// defined by black/openbox
const unsigned long XWindow::PropBlackboxAttributesElements;
const unsigned long XWindow::AttribDecoration;
const unsigned long XWindow::DecorNone;
const unsigned long XWindow::DecorNormal;


XWindow::XWindow(Window window, Netclient * netclient,
                 const bt::ScreenInfo & screenInfo, bt::Application & app)
  : _window(window), _netclient(netclient), _screenInfo(screenInfo),
    _app(app) {

  _unmapped = false;

  _display=_screenInfo.display().XDisplay();
  _root = _screenInfo.rootWindow();

  XSelectInput(_display, _window,
               PropertyChangeMask | StructureNotifyMask);

  // add an event handler for our window
  _app.insertEventHandler(_window, this);  

  updateBlackboxAttributes();
  updateNormalHints();
  updateWMHints();
  updateMotifHints();
  updateDimensions();
  updateState();
  updateDesktop();
  updateTitle();
  updateClass();

}


XWindow::~XWindow() {
  if (! _unmapped)
    XSelectInput(_display, _window, None);

  // tell our main app about our death
  _app.removeEventHandler( _window );
}

// Window configure (size, position, stacking, etc.).
void XWindow::configureNotifyEvent(const XConfigureEvent * const e) {
  updateDimensions();
}

// Window property changed/added/deleted.
void XWindow::propertyNotifyEvent(const XPropertyEvent * const e) {
  
  if (e->atom == XA_WM_NORMAL_HINTS)
    updateNormalHints();
  else if (e->atom == XA_WM_HINTS)
    updateWMHints();
  else if (e->atom == _netclient->xaBlackboxAttributes())
    updateBlackboxAttributes();  
  else if (e->atom == _netclient->wmState() )
    updateState();
  else if (e->atom == _netclient->wmDesktop() )
    updateDesktop();
  else if (e->atom == _netclient->wmName() ||
           e->atom == _netclient->xaWmName() )
    updateTitle();
  else if (e->atom == _netclient->xaWmClass() )
    updateClass();
  else if (e->atom == _netclient->xaMotifWmHints() )
    updateMotifHints();
}

// Window hidden.
void XWindow::unmapNotifyEvent(const XUnmapEvent * const e) {
  _unmapped = true;
}

// Window destroyed.
void XWindow::destroyNotifyEvent(const XDestroyWindowEvent * const e) {
  _unmapped = true;
}

void XWindow::updateDimensions(const XConfigureEvent * const e) {
  _rect.setRect(e->x, e->y, e->width, e->height);
}
  
void XWindow::updateDimensions() {

  XWindowAttributes win_attributes;
  Window junkwin;
  int rx, ry;

  _rect.setRect(0,0,1,1);

  if (! XGetWindowAttributes(_display, _window, &win_attributes)) {
    std::cerr << BBTOOL << ": " << "updateDimensions. couldn't get what I needed, so setting to ridiculously wrong values.\n";
    return;
  }

  XTranslateCoordinates (_display, _window, win_attributes.root, 
                         -win_attributes.border_width,
                         -win_attributes.border_width,
                         &rx, &ry, &junkwin);
  _rect.setRect(rx, ry, win_attributes.width, win_attributes.height);
  
}

void XWindow::updateBlackboxAttributes() {
  unsigned long *data;
  unsigned long num = PropBlackboxAttributesElements;

  _decorated = true;

  if (_netclient->getValue(_window,
                           _netclient->xaBlackboxAttributes(),
                           _netclient->xaBlackboxAttributes(), 
                           num, &data)) {
    if (num == PropBlackboxAttributesElements)
      if (data[0] & AttribDecoration)
        _decorated = (data[4] != DecorNone);
    delete data;
  }
}

void XWindow::updateNormalHints() {
  XSizeHints size;
  long ret;

  // defaults
  _gravity = NorthWestGravity;
  _inc_x = _inc_y = 1;
  _base_x = _base_y = 0;
  
  if (XGetWMNormalHints(_display, _window, &size, &ret)) {
    if (size.flags & PWinGravity)
      _gravity = size.win_gravity;
    if (size.flags & PBaseSize) {
      _base_x = size.base_width;
      _base_y = size.base_height;
    }
    if (size.flags & PResizeInc) {
      _inc_x = size.width_inc;
      _inc_y = size.height_inc;
    }
  }
}


void XWindow::updateMotifHints() {

// copied straight from blackbox's Window.cc, but hopefully nyz will move
// this somewhere shareable....  =:D

  /*
    this structure only contains 3 elements, even though the Motif 2.0
    structure contains 5, because we only use the first 3
  */
  struct PropMotifhints {
    unsigned long flags;
    unsigned long functions;
    unsigned long decorations;
  };
  static const unsigned int PROP_MWM_HINTS_ELEMENTS = 3u;
  enum { // MWM flags
    MWM_HINTS_FUNCTIONS   = 1<<0,
    MWM_HINTS_DECORATIONS = 1<<1
  };
  enum { // MWM functions
    MWM_FUNC_ALL      = 1<<0,
    MWM_FUNC_RESIZE   = 1<<1,
    MWM_FUNC_MOVE     = 1<<2,
    MWM_FUNC_MINIMIZE = 1<<3,
    MWM_FUNC_MAXIMIZE = 1<<4,
    MWM_FUNC_CLOSE    = 1<<5
  };
  enum { // MWM decorations
    MWM_DECOR_ALL      = 1<<0,
    MWM_DECOR_BORDER   = 1<<1,
    MWM_DECOR_RESIZEH  = 1<<2,
    MWM_DECOR_TITLE    = 1<<3,
    MWM_DECOR_MENU     = 1<<4,
    MWM_DECOR_MINIMIZE = 1<<5,
    MWM_DECOR_MAXIMIZE = 1<<6
  };

  Atom atom_return;
  PropMotifhints *prop = 0;
  int format;
  unsigned long num, len;
  int ret = XGetWindowProperty(_display, _window,
                               _netclient->xaMotifWmHints(), 0,
                               PROP_MWM_HINTS_ELEMENTS, False,
                               _netclient->xaMotifWmHints(), &atom_return,
                               &format, &num, &len,
                               (unsigned char **) &prop);

  if (ret != Success || !prop || num != PROP_MWM_HINTS_ELEMENTS) {
    if (prop) XFree(prop);
    return;
  }

  if (prop->flags & MWM_HINTS_DECORATIONS) {
    if (prop->decorations & MWM_DECOR_ALL) {
      _decorated = true;
    } else {
      _decorated = false;
    }
  }

  XFree(prop);


}

void XWindow::updateWMHints() {
  XWMHints *hints;

  // assume a window takes input if it doesnt specify
  _can_focus = True;
  
  if ((hints = XGetWMHints(_display, _window)) != NULL) {
    if (hints->flags & InputHint)
      _can_focus = hints->input;
    XFree(hints);
  }
}


void XWindow::updateState() {
  // set the defaults
  _shaded = _skip_pager = _iconic = _max_vert = _max_horz = false;
  
  unsigned long num = (unsigned) -1;
  Atom *state;
  if (! _netclient->getValue(_window, _netclient->wmState(), XA_ATOM,
                             num, &state))
    return;
  for (unsigned long i = 0; i < num; ++i) {
    if (state[i] == _netclient->wmStateMaximizedVert())
      _max_vert = true;
    if (state[i] == _netclient->wmStateMaximizedHorz())
      _max_horz = true;
    if (state[i] == _netclient->wmStateShaded())
      _shaded = true;
    if (state[i] == _netclient->wmStateHidden())
      _iconic = true;
    if (state[i] == _netclient->wmStateSkipPager())
      _skip_pager = true;
  }

  delete [] state;
}


void XWindow::updateDesktop() {
  unsigned long d = 0ul;
  if (! _netclient->getValue(_window, _netclient->wmDesktop(), XA_CARDINAL, d))
    d = 0ul;

  _desktop = static_cast<unsigned int>(d);

}


void XWindow::updateTitle() {
  _title = bt::toUnicode("");
  
  // try netwm
  std::string s;
  if (_netclient->getValue(_window, _netclient->wmName(),
                           Netclient::utf8, s)) {
    _title = bt::toUtf32(s);
  } else {
    // try old x stuff
    if (_netclient->getValue(_window, XA_WM_NAME, Netclient::ansi, s))
      _title = bt::toUnicode(s);
  }
  
  if (_title.empty())
    _title = bt::toUnicode("Unnamed");

}


void XWindow::updateClass() {
  // set the defaults
  _app_name = _app_class = "";

  Netclient::StringVect v;
  unsigned long num = 2;

  if (! _netclient->getValue(_window, XA_WM_CLASS, Netclient::ansi, num, v))
    return;

  if (num > 0) _app_name = v[0];
  if (num > 1) _app_class = v[1];
}



void XWindow::shade(const bool sh) const {
  _netclient->sendClientMessage(_root, _netclient->wmState(),
                                _window, (sh ? 1 : 0),
                                _netclient->wmStateShaded());
}

void XWindow::close() const {
  _netclient->sendClientMessage(_root, _netclient->closeWindow(),
                                _window);
}


void XWindow::raise() const {
  XRaiseWindow(_display, _window);
}

void XWindow::lower() const {
  XLowerWindow(_display, _window);
}

void XWindow::iconify() const {
  _netclient->sendClientMessage(_root, _netclient->xaWmChangeState(),
                                _window, IconicState);
}

void XWindow::focus(bool raise) const {
  // this will cause the window to be uniconified also

  if (raise) {
    _netclient->sendClientMessage(_root, _netclient->activeWindow(),
                                  _window);
  } else {
    XSetInputFocus(_display, _window, None, CurrentTime);
  }
}

void XWindow::decorate(bool d) const {
                                
  // YAY trolltech!!!  =:)
  // http://lists.trolltech.com/qt-interest/1999-06/thread00047-0.html
  long prop[5] = {2, 1, 1, 0, 0};

  if (_decorated)
          prop[2] = 0;
  else
          prop[2] = 1;

  XChangeProperty(_display, _window, 
    _netclient->xaMotifWmHints(),
    _netclient->xaMotifWmHints(),
    32, 0, (unsigned char *) prop, 5);
}

void XWindow::sendTo(unsigned int dest) const {
  _netclient->sendClientMessage(_root, _netclient->wmDesktop(),
                                _window, dest);
}


void XWindow::move(int x, int y) const {

  XWindowAttributes win_attributes;
  Window junkwin;
  int rx, ry;


  if (! XGetWindowAttributes(_display, _window, &win_attributes)) {
    std::cerr << BBTOOL << ": " << "move: couldn't get what I needed. not able to move, sorry.\n";
    return;
  }

  XTranslateCoordinates (_display, _window, win_attributes.root, 
                         -win_attributes.border_width,
                         -win_attributes.border_width,
                         &rx, &ry, &junkwin);
      
  Status status;
  int xright, ybelow;
  int dw = _screenInfo.width(), dh = _screenInfo.height();

  
  /* find our window manager frame, if any */
  Window wmframe = _window;

  while (True) {
    Window root, parent;
    Window *childlist;
    unsigned int ujunk;

    status = XQueryTree(_display, wmframe, &root, &parent, &childlist, &ujunk);
    if (parent == root || !parent || !status)
      break;
    wmframe = parent;
    if (status && childlist)
      XFree((char *)childlist);
  }

  if (wmframe != _window) {
    /* WM reparented, so find edges of the frame */
    /* Only works for ICCCM-compliant WMs, and then only if the
       window has corner gravity.  We would need to know the original width
       of the window to correctly handle the other gravities. */

    XWindowAttributes frame_attr;

    if (!XGetWindowAttributes(_display, wmframe, &frame_attr)) {
      std::cerr << BBTOOL << ": " << "updateDimensions. error. can't get frame attributes.\n";
    }
    
    switch (_gravity) {
    case NorthWestGravity: case SouthWestGravity:
    case NorthEastGravity: case SouthEastGravity:
    case WestGravity:
      rx = frame_attr.x;
    }
    switch (_gravity) {
    case NorthWestGravity: case SouthWestGravity:
    case NorthEastGravity: case SouthEastGravity:
    case EastGravity:
      xright = dw - frame_attr.x - frame_attr.width -
        2*frame_attr.border_width;
    }
    switch (_gravity) {
    case NorthWestGravity: case SouthWestGravity:
    case NorthEastGravity: case SouthEastGravity:
    case NorthGravity:
      ry = frame_attr.y;
    }
    switch (_gravity) {
    case NorthWestGravity: case SouthWestGravity:
    case NorthEastGravity: case SouthEastGravity:
    case SouthGravity:
      ybelow = dh - frame_attr.y - frame_attr.height -
        2*frame_attr.border_width;
    }
  }

  int destx = rx +x, desty = ry +y;

  XMoveWindow(_display, _window, destx, desty);

}


void XWindow::resizeRel(int dwidth, int dheight) const {
  // resize in increments if requested by the window
  unsigned int width = _rect.width(), height = _rect.height();
  
  unsigned int wdest = width + (dwidth * _inc_x) / _inc_x * _inc_x + _base_x;
  unsigned int hdest = height + (dheight * _inc_y) / _inc_y * _inc_y + _base_y;

  XResizeWindow(_display, _window, wdest, hdest);
}


void XWindow::resizeAbs(unsigned int width, unsigned int height) const {
  // resize in increments if requested by the window

  unsigned int wdest = width / _inc_x * _inc_x + _base_x;
  unsigned int hdest = height / _inc_y * _inc_y + _base_y;

  if (width > wdest) {
    while (width > wdest)
      wdest += _inc_x;
  } else {
    while (width < wdest)
      wdest -= _inc_x;
  }
  if (height > hdest) {
    while (height > hdest)
      hdest += _inc_y;
  } else {
    while (height < hdest)
      hdest -= _inc_y;
  }
  
  XResizeWindow(_display, _window, wdest, hdest);
}


void XWindow::toggleMaximize(Max max) const {
  switch (max) {
  case Max_Full:
    _netclient->
      sendClientMessage(_root, _netclient->wmState(),
                        _window, (_max_vert == _max_horz ? 2 : 1),
                        _netclient->wmStateMaximizedHorz(),
                        _netclient->wmStateMaximizedVert());
    break;

  case Max_Horz:
    _netclient->
      sendClientMessage(_root, _netclient->wmState(),
                        _window, 2,
                        _netclient->wmStateMaximizedHorz());
    break;

  case Max_Vert:
    _netclient->
      sendClientMessage(_root, _netclient->wmState(),
                        _window, 2,
                        _netclient->wmStateMaximizedVert());
    break;
    
  case Max_None:
    assert(false);  // you should not do this. it is pointless and probly a bug
    break;
  }
}


void XWindow::maximize(Max max) const {
  switch (max) {
  case Max_None:
    _netclient->
      sendClientMessage(_root, _netclient->wmState(),
                        _window, 0,
                        _netclient->wmStateMaximizedHorz(),
                        _netclient->wmStateMaximizedVert());
    break;

  case Max_Full:
    _netclient->
      sendClientMessage(_root, _netclient->wmState(),
                        _window, 1,
                        _netclient->wmStateMaximizedHorz(),
                        _netclient->wmStateMaximizedVert());
    break;

  case Max_Horz:
    _netclient->
      sendClientMessage(_root, _netclient->wmState(),
                        _window, 1,
                        _netclient->wmStateMaximizedHorz());
    break;

  case Max_Vert:
    _netclient->
      sendClientMessage(_root, _netclient->wmState(),
                        _window, 1,
                        _netclient->wmStateMaximizedVert());
    break;
  }
}


Generated by  Doxygen 1.6.0   Back to index