]> Dogcows Code - chaz/openbox/blobdiff - src/client.cc
dont put DEBUG: in the printf
[chaz/openbox] / src / client.cc
index 572c383aecb15d5f2410164c834f877f0349454e..f9676e7cf7eebd3bbb8cc87722d5f84cb80c7cb5 100644 (file)
@@ -1,8 +1,6 @@
 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
 
-#ifdef HAVE_CONFIG_H
-# include "../config.h"
-#endif
+#include "config.h"
 
 #include "client.hh"
 #include "frame.hh"
@@ -17,19 +15,19 @@ extern "C" {
 #include <X11/Xutil.h>
 #include <X11/Xatom.h>
 
-#include <assert.h>
-
 #include "gettext.h"
 #define _(str) gettext(str)
 }
 
+#include <cstring> // for memcpy
+#include <climits>
+#include <cassert>
 #include <algorithm>
 
 namespace ob {
 
 Client::Client(int screen, Window window)
   : otk::EventHandler(),
-    WidgetBase(WidgetBase::Type_Client),
     frame(0), _screen(screen), _window(window)
 {
   assert(screen >= 0);
@@ -39,35 +37,37 @@ Client::Client(int screen, Window window)
   
   // update EVERYTHING the first time!!
 
-  // we default to NormalState, visible
+  // defaults
   _wmstate = NormalState;
-  // start unfocused
   _focused = false;
-  // not a transient by default of course
   _transient_for = 0;
-  // pick a layer to start from
   _layer = Layer_Normal;
-  // default to not urgent
   _urgent = false;
+  _positioned = false;
+  _disabled_decorations = 0;
+  _group = None;
+  _desktop = 0;
+  _nicons = 0;
   
   getArea();
   getDesktop();
+  getState();  // do this before updateTransientFor! (for _modal)
+  getShaped();
 
   updateTransientFor();
-  getType();
   getMwmHints();
-
-  getState();
-  getShaped();
+  getType(); // this can change the mwmhints for special cases
 
   updateProtocols();
 
-  // got the type, the mwmhints, and the protocols, so we're ready to set up
+  getGravity();        // get the attribute gravity
+  updateNormalHints(); // this may override the attribute gravity
+
+  // got the type, the mwmhints, the protocols, and the normal hints (min/max
+  // sizes), so we're ready to set up
   // the decorations/functions
   setupDecorAndFunctions();
   
-  getGravity();        // get the attribute gravity
-  updateNormalHints(); // this may override the attribute gravity
   // also get the initial_state and set _iconic if we aren't "starting"
   // when we're "starting" that means we should use whatever state was already
   // on the window over the initial map state, because it was already mapped
@@ -76,7 +76,8 @@ Client::Client(int screen, Window window)
   updateIconTitle();
   updateClass();
   updateStrut();
-
+  updateIcons();
+  
   // this makes sure that these windows appear on all desktops
   if (/*_type == Type_Dock ||*/ _type == Type_Desktop)
     _desktop = 0xffffffff;
@@ -85,19 +86,23 @@ Client::Client(int screen, Window window)
   // any changes we've made here
   otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
                      otk::Property::atoms.cardinal, (unsigned)_desktop);
-  
   changeState();
 }
 
 
 Client::~Client()
 {
+  assert(_nicons > 0); // there should always be a default..
+  for (int j = 0; j < _nicons; ++j)
+    delete [] _icons[j].data;
+  delete [] _icons;
+  
   // clean up childrens' references
   while (!_transients.empty()) {
     _transients.front()->_transient_for = 0;
     _transients.pop_front();
   }
-  
+
   // clean up parents reference to this
   if (_transient_for)
     _transient_for->_transients.remove(this); // remove from old parent
@@ -115,6 +120,21 @@ Client::~Client()
 }
 
 
+bool Client::validate() const
+{
+  XSync(**otk::display, false); // get all events on the server
+
+  XEvent e;
+  if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &e) ||
+      XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &e)) {
+    XPutBackEvent(**otk::display, &e);
+    return false;
+  }
+
+  return true;
+}
+
+
 void Client::getGravity()
 {
   XWindowAttributes wattrib;
@@ -167,9 +187,12 @@ void Client::getType()
         _type = Type_Dialog;
       else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
         _type = Type_Normal;
-//    XXX: make this work again
-//    else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
-//      mwm_decorations = 0; // prevent this window from getting any decor
+      else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override){
+        // prevent this window from getting any decor or functionality
+        _mwmhints.flags &= MwmFlag_Functions | MwmFlag_Decorations;
+        _mwmhints.decorations = 0;
+        _mwmhints.functions = 0;
+      }
       if (_type != (WindowType) -1)
         break; // grab the first known type
     }
@@ -192,7 +215,7 @@ void Client::getType()
 void Client::setupDecorAndFunctions()
 {
   // start with everything (cept fullscreen)
-  _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
+  _decorations = Decor_Titlebar | Decor_Handle | Decor_Border | Decor_Icon |
     Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
   _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
     Func_Shade;
@@ -201,8 +224,9 @@ void Client::setupDecorAndFunctions()
     _functions |= Func_Close;
   }
 
-  if (_min_size.x() > _max_size.x() || _min_size.y() > _max_size.y()) {
-    _decorations &= ~Decor_Maximize;
+  if (!(_min_size.width() < _max_size.width() ||
+        _min_size.height() < _max_size.height())) {
+    _decorations &= ~(Decor_Maximize | Decor_Handle);
     _functions &= ~(Func_Resize | Func_Maximize);
   }
   
@@ -211,6 +235,7 @@ void Client::setupDecorAndFunctions()
     // normal windows retain all of the possible decorations and
     // functionality, and are the only windows that you can fullscreen
     _functions |= Func_Fullscreen;
+    break;
 
   case Type_Dialog:
     // dialogs cannot be maximized
@@ -243,11 +268,8 @@ void Client::setupDecorAndFunctions()
         _decorations &= ~Decor_Border;
       if (! (_mwmhints.decorations & MwmDecor_Handle))
         _decorations &= ~Decor_Handle;
-      if (! (_mwmhints.decorations & MwmDecor_Title)) {
+      if (! (_mwmhints.decorations & MwmDecor_Title))
         _decorations &= ~Decor_Titlebar;
-        // if we don't have a titlebar, then we cannot shade!
-        _functions &= ~Func_Shade;
-      }
       if (! (_mwmhints.decorations & MwmDecor_Iconify))
         _decorations &= ~Decor_Iconify;
       if (! (_mwmhints.decorations & MwmDecor_Maximize))
@@ -271,7 +293,39 @@ void Client::setupDecorAndFunctions()
     }
   }
 
+  // can't maximize without moving/resizing
+  if (!((_functions & Func_Move) && (_functions & Func_Resize)))
+    _functions &= ~Func_Maximize;
+
+  // finally, user specified disabled decorations are applied to subtract
+  // decorations
+  if (_disabled_decorations & Decor_Titlebar)
+    _decorations &= ~Decor_Titlebar;
+  if (_disabled_decorations & Decor_Handle)
+    _decorations &= ~Decor_Handle;
+  if (_disabled_decorations & Decor_Border)
+    _decorations &= ~Decor_Border;
+  if (_disabled_decorations & Decor_Iconify)
+    _decorations &= ~Decor_Iconify;
+  if (_disabled_decorations & Decor_Maximize)
+    _decorations &= ~Decor_Maximize;
+  if (_disabled_decorations & Decor_AllDesktops)
+    _decorations &= ~Decor_AllDesktops;
+  if (_disabled_decorations & Decor_Close)
+    _decorations &= ~Decor_Close;
+
+  // if we don't have a titlebar, then we cannot shade!
+  if (!(_decorations & Decor_Titlebar))
+    _functions &= ~Func_Shade;
+
   changeAllowedActions();
+
+  if (frame) {
+    frame->adjustSize(); // change the decors on the frame
+    frame->adjustPosition(); // with more/less decorations, we may need to be
+                             // moved
+    remaximize(); // with new decor, the window's maximized size may change
+  }
 }
 
 
@@ -306,7 +360,7 @@ void Client::getArea()
   ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
   assert(ret != BadWindow);
 
-  _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
+  _area = otk::Rect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
   _border_width = wattrib.border_width;
 }
 
@@ -348,7 +402,6 @@ void Client::getState()
   }
 }
 
-
 void Client::getShaped()
 {
   _shaped = false;
@@ -367,12 +420,40 @@ void Client::getShaped()
 #endif // SHAPE
 }
 
+Client *Client::searchFocusTree(Client *node, Client *skip)
+{
+  List::const_iterator it, end = node->_transients.end();
+  Client *ret;
+
+  for (it = node->_transients.begin(); it != end; ++it) {
+    if (*it == skip) continue; // circular?
+    if ((ret = searchModalTree(*it, skip))) return ret; // got one
+    if ((*it)->_focused) return *it; // got one
+  }
+  return 0;
+}
 
 void Client::calcLayer() {
   StackLayer l;
+  bool fs = false;
 
+  // are we fullscreen, or do we have a fullscreen transient parent?
+  Client *c = this;
+  while (c) {
+    if (c->_fullscreen) {
+      fs =true;
+      break;
+    }
+    c = c->_transient_for;
+  }
+  if (!fs && _fullscreen) {
+    // is one of our transients focused?
+    c = searchFocusTree(this, this);
+    if (c) fs = true;
+  }
+  
   if (_iconic) l = Layer_Icon;
-  else if (_fullscreen) l = Layer_Fullscreen;
+  else if (fs) l = Layer_Fullscreen;
   else if (_type == Type_Desktop) l = Layer_Desktop;
   else if (_type == Type_Dock) {
     if (!_below) l = Layer_Top;
@@ -394,7 +475,6 @@ void Client::calcLayer() {
   }
 }
 
-
 void Client::updateProtocols()
 {
   Atom *proto;
@@ -417,7 +497,6 @@ void Client::updateProtocols()
   }
 }
 
-
 void Client::updateNormalHints()
 {
   XSizeHints size;
@@ -427,13 +506,10 @@ void Client::updateNormalHints()
   // defaults
   _min_ratio = 0.0;
   _max_ratio = 0.0;
-  _size_inc.setPoint(1, 1);
-  _base_size.setPoint(0, 0);
-  _min_size.setPoint(0, 0);
-  _max_size.setPoint(INT_MAX, INT_MAX);
-
-  // XXX: might want to cancel any interactive resizing of the window at this
-  // point..
+  _size_inc = otk::Size(1, 1);
+  _base_size = otk::Size(0, 0);
+  _min_size = otk::Size(0, 0);
+  _max_size = otk::Size(INT_MAX, INT_MAX);
 
   // get the hints from the window
   if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) {
@@ -446,9 +522,9 @@ void Client::updateNormalHints()
       // changing its gravity
       if (frame && _gravity != oldgravity) {
         // move our idea of the client's position based on its new gravity
-        int x, y;
+        int x = frame->area().x(), y = frame->area().y();
         frame->frameGravity(x, y);
-        _area.setPos(x, y);
+        _area = otk::Rect(otk::Point(x, y), _area.size());
       }
     }
 
@@ -458,20 +534,19 @@ void Client::updateNormalHints()
     }
 
     if (size.flags & PMinSize)
-      _min_size.setPoint(size.min_width, size.min_height);
+      _min_size = otk::Size(size.min_width, size.min_height);
     
     if (size.flags & PMaxSize)
-      _max_size.setPoint(size.max_width, size.max_height);
+      _max_size = otk::Size(size.max_width, size.max_height);
     
     if (size.flags & PBaseSize)
-      _base_size.setPoint(size.base_width, size.base_height);
+      _base_size = otk::Size(size.base_width, size.base_height);
     
     if (size.flags & PResizeInc)
-      _size_inc.setPoint(size.width_inc, size.height_inc);
+      _size_inc = otk::Size(size.width_inc, size.height_inc);
   }
 }
 
-
 void Client::updateWMHints(bool initstate)
 {
   XWMHints *hints;
@@ -506,7 +581,7 @@ void Client::updateWMHints(bool initstate)
   if (ur != _urgent) {
     _urgent = ur;
 #ifdef DEBUG
-    printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
+    printf("Urgent Hint for 0x%lx: %s\n",
            (long)_window, _urgent ? "ON" : "OFF");
 #endif
     // fire the urgent callback if we're mapped, otherwise, wait until after
@@ -516,7 +591,6 @@ void Client::updateWMHints(bool initstate)
   }
 }
 
-
 void Client::updateTitle()
 {
   _title = "";
@@ -533,10 +607,9 @@ void Client::updateTitle()
     _title = _("Unnamed Window");
 
   if (frame)
-    frame->setTitle(_title);
+    frame->adjustTitle();
 }
 
-
 void Client::updateIconTitle()
 {
   _icon_title = "";
@@ -553,7 +626,6 @@ void Client::updateIconTitle()
     _icon_title = _("Unnamed Window");
 }
 
-
 void Client::updateClass()
 {
   // set the defaults
@@ -576,7 +648,6 @@ void Client::updateClass()
   }
 }
 
-
 void Client::updateStrut()
 {
   unsigned long num = 4;
@@ -590,14 +661,16 @@ void Client::updateStrut()
     _strut.right = data[1];
     _strut.top = data[2];
     _strut.bottom = data[3]; 
-   
-    openbox->screen(_screen)->updateStrut();
+
+    // updating here is pointless while we're being mapped cuz we're not in
+    // the screen's client list yet
+    if (frame)
+      openbox->screen(_screen)->updateStruts();
   }
 
   delete [] data;
 }
 
-
 void Client::updateTransientFor()
 {
   Window t = 0;
@@ -627,15 +700,74 @@ void Client::updateTransientFor()
     _transient_for = c;
     if (_transient_for)
       _transient_for->_transients.push_back(this); // add to new parent
-
-    // XXX: change decor status?
   }
 }
 
+void Client::updateIcons()
+{
+  unsigned long num = (unsigned) -1;
+  unsigned long *data;
+  unsigned long w, h, i = 0;
+  bool freeit = false;
+
+  for (int j = 0; j < _nicons; ++j)
+    delete [] _icons[j].data;
+  if (_nicons > 0)
+    delete [] _icons;
+  _nicons = 0;
+
+  if (!otk::Property::get(_window, otk::Property::atoms.net_wm_icon,
+                          otk::Property::atoms.cardinal, &num, &data)) {
+    // use default icon(s)
+    num = openbox->screen(_screen)->config().icon_length;
+    data = openbox->screen(_screen)->config().default_icon;
+  } else
+    freeit = true;
+  
+  // figure out how man valid icons are in here
+  while (num - i > 2) {
+    w = data[i++];
+    h = data[i++];
+    i += w * h;
+    if (i > num) break;
+    ++_nicons;
+  }
+
+  _icons = new Icon[_nicons];
+
+  // store the icons
+  i = 0;
+  for (int j = 0; j < _nicons; ++j) {
+    w = _icons[j].w = data[i++];
+      h = _icons[j].h = data[i++];
+      _icons[j].data = new unsigned long[w * h];
+      ::memcpy(_icons[j].data, &data[i], w * h * sizeof(unsigned long));
+      i += w * h;
+      assert(i <= num);
+  }
+
+  if (freeit)
+    delete [] data;
+
+  if (_nicons <= 0) {
+    _nicons = 1;
+    _icons = new Icon[1];
+    _icons[i].w = 0;
+    _icons[i].h = 0;
+    _icons[i].data = 0;
+  }
+
+  assert(_nicons > 0); // there should always be a default..
+  
+  if (frame) frame->adjustIcon();
+}
 
 void Client::propertyHandler(const XPropertyEvent &e)
 {
   otk::EventHandler::propertyHandler(e);
+
+  // validate cuz we query stuff off the client here
+  if (!validate()) return;
   
   // compress changes to a single property into a single change
   XEvent ce;
@@ -658,7 +790,6 @@ void Client::propertyHandler(const XPropertyEvent &e)
     getType();
     calcLayer(); // type may have changed, so update the layer
     setupDecorAndFunctions();
-    frame->adjustSize(); // this updates the frame for any new decor settings
   }
   else if (e.atom == otk::Property::atoms.net_wm_name ||
            e.atom == otk::Property::atoms.wm_name)
@@ -671,79 +802,68 @@ void Client::propertyHandler(const XPropertyEvent &e)
   else if (e.atom == otk::Property::atoms.wm_protocols) {
     updateProtocols();
     setupDecorAndFunctions();
-    frame->adjustSize(); // update the decorations
   }
   else if (e.atom == otk::Property::atoms.net_wm_strut)
     updateStrut();
+  else if (e.atom == otk::Property::atoms.net_wm_icon)
+    updateIcons();
 }
 
-
 void Client::setWMState(long state)
 {
   if (state == _wmstate) return; // no change
   
   switch (state) {
   case IconicState:
-    setDesktop(ICONIC_DESKTOP);
+    iconify(true);
     break;
   case NormalState:
-    setDesktop(openbox->screen(_screen)->desktop());
+    iconify(false);
     break;
   }
 }
 
-
-void Client::setDesktop(long target)
+void Client::setDesktop(unsigned int target)
 {
   if (target == _desktop) return;
   
-  printf("Setting desktop %ld\n", target);
+  printf("Setting desktop %u\n", target);
 
-  if (!(target >= 0 || target == (signed)0xffffffff ||
-        target == ICONIC_DESKTOP))
+  if (!(target < openbox->screen(_screen)->numDesktops() ||
+        target == 0xffffffff))
     return;
-  
-  _desktop = target;
 
-  // set the desktop hint, but not if we're iconifying
-  if (_desktop != ICONIC_DESKTOP)
-    otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
-                       otk::Property::atoms.cardinal, (unsigned)_desktop);
-  
+  _desktop = target;
+  // set the desktop hint
+  otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
+                     otk::Property::atoms.cardinal, _desktop);
+  frame->adjustState(); // the frame can display the current desktop state
   // 'move' the window to the new desktop
-  if (_desktop == openbox->screen(_screen)->desktop() ||
-      _desktop == (signed)0xffffffff)
-    frame->show();
-  else
-    frame->hide();
-
-  // Handle Iconic state. Iconic state is maintained by the client being a
-  // member of the ICONIC_DESKTOP, so this is where we make iconifying and
-  // uniconifying happen.
-  bool i = _desktop == ICONIC_DESKTOP;
-  if (i != _iconic) { // has the state changed?
-    _iconic = i;
-    if (_iconic) {
-      _wmstate = IconicState;
-      ignore_unmaps++;
-      // we unmap the client itself so that we can get MapRequest events, and
-      // because the ICCCM tells us to!
-      XUnmapWindow(**otk::display, _window);
-    } else {
-      _wmstate = NormalState;
-      XMapWindow(**otk::display, _window);
-    }
-    changeState();
-  }
-  
-  frame->adjustState();
+  showhide();
+  openbox->screen(_screen)->updateStruts();
 }
 
+void Client::showhide()
+{
+  bool show;
+  Screen *s = openbox->screen(_screen);
+
+  if (_iconic) show = false;
+  else if (!(_desktop == s->desktop() ||
+             _desktop == 0xffffffff)) show = false;
+  else if (normal() && s->showingDesktop()) show = false;
+  else show = true;
+
+  if (show) frame->show();
+  else      frame->hide();
+}
 
 void Client::setState(StateAction action, long data1, long data2)
 {
   bool shadestate = _shaded;
   bool fsstate = _fullscreen;
+  bool maxh = _max_horz;
+  bool maxv = _max_vert;
 
   if (!(action == State_Add || action == State_Remove ||
         action == State_Toggle))
@@ -780,15 +900,11 @@ void Client::setState(StateAction action, long data1, long data2)
       if (state == otk::Property::atoms.net_wm_state_modal) {
         if (_modal) continue;
         _modal = true;
-        // XXX: give it focus if another window has focus that shouldnt now
       } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
-        if (_max_vert) continue;
-        _max_vert = true;
-        // XXX: resize the window etc
+        maxv = true;
       } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
         if (_max_horz) continue;
-        _max_horz = true;
-        // XXX: resize the window etc
+        maxh = true;
       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
         shadestate = true;
       } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
@@ -810,13 +926,9 @@ void Client::setState(StateAction action, long data1, long data2)
         if (!_modal) continue;
         _modal = false;
       } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
-        if (!_max_vert) continue;
-        _max_vert = false;
-        // XXX: resize the window etc
+        maxv = false;
       } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
-        if (!_max_horz) continue;
-        _max_horz = false;
-        // XXX: resize the window etc
+        maxh = false;
       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
         shadestate = false;
       } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
@@ -834,16 +946,31 @@ void Client::setState(StateAction action, long data1, long data2)
       }
     }
   }
+  if (maxh != _max_horz || maxv != _max_vert) {
+    if (maxh != _max_horz && maxv != _max_vert) { // toggling both
+      if (maxh == maxv) { // both going the same way
+        maximize(maxh, 0, true);
+      } else {
+        maximize(maxh, 1, true);
+        maximize(maxv, 2, true);
+      }
+    } else { // toggling one
+      if (maxh != _max_horz)
+        maximize(maxh, 1, true);
+      else
+        maximize(maxv, 2, true);
+    }
+  }
   // change fullscreen state before shading, as it will affect if the window
   // can shade or not
   if (fsstate != _fullscreen)
-    fullscreen(fsstate);
+    fullscreen(fsstate, true);
   if (shadestate != _shaded)
     shade(shadestate);
   calcLayer();
+  changeState(); // change the hint to relect these changes
 }
 
-
 void Client::toggleClientBorder(bool addborder)
 {
   // adjust our idea of where the client is, based on its border. When the
@@ -851,7 +978,8 @@ void Client::toggleClientBorder(bool addborder)
   // different position.
   // when re-adding the border to the client, the same operation needs to be
   // reversed.
-  int x = _area.x(), y = _area.y();
+  int oldx = _area.x(), oldy = _area.y();
+  int x = oldx, y = oldy;
   switch(_gravity) {
   default:
   case NorthWestGravity:
@@ -894,22 +1022,25 @@ void Client::toggleClientBorder(bool addborder)
     else           y += _border_width;
     break;
   }
-  _area.setPos(x, y);
+  _area = otk::Rect(otk::Point(x, y), _area.size());
 
   if (addborder) {
     XSetWindowBorderWidth(**otk::display, _window, _border_width);
 
     // move the client so it is back it the right spot _with_ its border!
-    XMoveWindow(**otk::display, _window, x, y);
+    if (x != oldx || y != oldy)
+      XMoveWindow(**otk::display, _window, x, y);
   } else
     XSetWindowBorderWidth(**otk::display, _window, 0);
 }
 
-
 void Client::clientMessageHandler(const XClientMessageEvent &e)
 {
   otk::EventHandler::clientMessageHandler(e);
   
+  // validate cuz we query stuff off the client here
+  if (!validate()) return;
+  
   if (e.format != 32) return;
 
   if (e.message_type == otk::Property::atoms.wm_change_state) {
@@ -964,17 +1095,31 @@ void Client::clientMessageHandler(const XClientMessageEvent &e)
 #ifdef DEBUG
     printf("net_active_window for 0x%lx\n", _window);
 #endif
+    if (openbox->screen(_screen)->showingDesktop())
+      openbox->screen(_screen)->showDesktop(false);
     if (_iconic)
-      setDesktop(openbox->screen(_screen)->desktop());
+      iconify(false);
+    else if (!frame->visible()) // if its not visible for other reasons, then
+      return;                   // don't mess with it
     if (_shaded)
       shade(false);
-    // XXX: deiconify
     focus();
     openbox->screen(_screen)->raiseWindow(this);
+  } else if (e.message_type == otk::Property::atoms.openbox_active_window) {
+    if (openbox->screen(_screen)->showingDesktop())
+      openbox->screen(_screen)->showDesktop(false);
+    if (_iconic)
+      iconify(false);
+    else if (!frame->visible()) // if its not visible for other reasons, then
+      return;                   // don't mess with it
+    if (e.data.l[0] && _shaded)
+      shade(false);
+    focus();
+    if (e.data.l[1])
+      openbox->screen(_screen)->raiseWindow(this);
   }
 }
 
-
 #if defined(SHAPE)
 void Client::shapeHandler(const XShapeEvent &e)
 {
@@ -987,34 +1132,39 @@ void Client::shapeHandler(const XShapeEvent &e)
 }
 #endif
 
-
 void Client::resize(Corner anchor, int w, int h)
 {
   if (!(_functions & Func_Resize)) return;
   internal_resize(anchor, w, h);
 }
 
-
-void Client::internal_resize(Corner anchor, int w, int h, bool user,
-                             int x, int y)
+void Client::internal_resize(Corner anchor, int w, int h,
+                             bool user, int x, int y)
 {
-  w -= _base_size.x(); 
-  h -= _base_size.y();
-
-  // for interactive resizing. have to move half an increment in each
-  // direction.
-  w += _size_inc.x() / 2;
-  h += _size_inc.y() / 2;
+  w -= _base_size.width();
+  h -= _base_size.height();
 
   if (user) {
+    // for interactive resizing. have to move half an increment in each
+    // direction.
+    int mw = w % _size_inc.width(); // how far we are towards the next size inc
+    int mh = h % _size_inc.height();
+    int aw = _size_inc.width() / 2; // amount to add
+    int ah = _size_inc.height() / 2;
+    // don't let us move into a new size increment
+    if (mw + aw >= _size_inc.width()) aw = _size_inc.width() - mw - 1;
+    if (mh + ah >= _size_inc.height()) ah = _size_inc.height() - mh - 1;
+    w += aw;
+    h += ah;
+    
     // if this is a user-requested resize, then check against min/max sizes
     // and aspect ratios
 
     // smaller than min size or bigger than max size?
-    if (w < _min_size.x()) w = _min_size.x();
-    else if (w > _max_size.x()) w = _max_size.x();
-    if (h < _min_size.y()) h = _min_size.y();
-    else if (h > _max_size.y()) h = _max_size.y();
+    if (w > _max_size.width()) w = _max_size.width();
+    if (w < _min_size.width()) w = _min_size.width();
+    if (h > _max_size.height()) h = _max_size.height();
+    if (h < _min_size.height()) h = _min_size.height();
 
     // adjust the height ot match the width for the aspect ratios
     if (_min_ratio)
@@ -1024,21 +1174,21 @@ void Client::internal_resize(Corner anchor, int w, int h, bool user,
   }
 
   // keep to the increments
-  w /= _size_inc.x();
-  h /= _size_inc.y();
+  w /= _size_inc.width();
+  h /= _size_inc.height();
 
   // you cannot resize to nothing
   if (w < 1) w = 1;
   if (h < 1) h = 1;
   
   // store the logical size
-  _logical_size.setPoint(w, h);
+  _logical_size = otk::Size(w, h);
 
-  w *= _size_inc.x();
-  h *= _size_inc.y();
+  w *= _size_inc.width();
+  h *= _size_inc.height();
 
-  w += _base_size.x();
-  h += _base_size.y();
+  w += _base_size.width();
+  h += _base_size.height();
 
   if (x == INT_MIN || y == INT_MIN) {
     x = _area.x();
@@ -1059,7 +1209,7 @@ void Client::internal_resize(Corner anchor, int w, int h, bool user,
     }
   }
 
-  _area.setSize(w, h);
+  _area = otk::Rect(_area.position(), otk::Size(w, h));
 
   XResizeWindow(**otk::display, _window, w, h);
 
@@ -1068,17 +1218,41 @@ void Client::internal_resize(Corner anchor, int w, int h, bool user,
   internal_move(x, y);
 }
 
+const Icon *Client::icon(const otk::Size &s) const
+{
+  unsigned long req = s.width() * s.height();
+  // si is the smallest image >= req
+  // li is the largest image < req
+  unsigned long smallest = 0xffffffff, largest = 0, si = 0, li = 0;
+
+  assert(_nicons > 0); // there should always be a default..
+  for (int i = 0; i < _nicons; ++i) {
+    unsigned long size = _icons[i].w * _icons[i].h;
+    if (size < smallest && size >= req) {
+      smallest = size;
+      si = i;
+    }
+    if (size > largest && size <= req) {
+      largest = size;
+      li = i;
+    }
+  }
+  if (smallest == 0xffffffff) // didnt find one bigger than us...
+    return &_icons[li];
+  return &_icons[si];
+}
 
 void Client::move(int x, int y)
 {
   if (!(_functions & Func_Move)) return;
+  frame->frameGravity(x, y); // get the client's position based on x,y for the
+                             // frame
   internal_move(x, y);
 }
 
-
 void Client::internal_move(int x, int y)
 {
-  _area.setPos(x, y);
+  _area = otk::Rect(otk::Point(x, y), _area.size());
 
   // move the frame to be in the requested position
   if (frame) { // this can be called while mapping, before frame exists
@@ -1091,19 +1265,26 @@ void Client::internal_move(int x, int y)
     event.xconfigure.display = **otk::display;
     event.xconfigure.event = _window;
     event.xconfigure.window = _window;
-    event.xconfigure.x = x;
-    event.xconfigure.y = y;
+    
+    // root window coords with border in mind
+    event.xconfigure.x = x - _border_width + frame->size().left;
+    event.xconfigure.y = y - _border_width + frame->size().top;
+    
     event.xconfigure.width = _area.width();
     event.xconfigure.height = _area.height();
     event.xconfigure.border_width = _border_width;
-    event.xconfigure.above = frame->window();
+    event.xconfigure.above = frame->plate();
     event.xconfigure.override_redirect = False;
     XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
                StructureNotifyMask, &event);
+#if 0//def DEBUG
+    printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
+           event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
+           event.xconfigure.height, event.xconfigure.window);
+#endif
   }
 }
 
-
 void Client::close()
 {
   XEvent ce;
@@ -1129,7 +1310,6 @@ void Client::close()
   XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
 }
 
-
 void Client::changeState()
 {
   unsigned long state[2];
@@ -1137,7 +1317,7 @@ void Client::changeState()
   state[1] = None;
   otk::Property::set(_window, otk::Property::atoms.wm_state,
                      otk::Property::atoms.wm_state, state, 2);
-  
+
   Atom netstate[10];
   int num = 0;
   if (_modal)
@@ -1169,7 +1349,6 @@ void Client::changeState()
     frame->adjustState();
 }
 
-
 void Client::changeAllowedActions(void)
 {
   Atom actions[9];
@@ -1196,8 +1375,37 @@ void Client::changeAllowedActions(void)
 
   otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
                      otk::Property::atoms.atom, actions, num);
+
+  // make sure the window isn't breaking any rules now
+  
+  if (!(_functions & Func_Shade) && _shaded)
+    if (frame) shade(false);
+    else _shaded = false;
+  if (!(_functions & Func_Iconify) && _iconic)
+    if (frame) setDesktop(openbox->screen(_screen)->desktop());
+    else _iconic = false;
+  if (!(_functions & Func_Fullscreen) && _fullscreen)
+    if (frame) fullscreen(false);
+    else _fullscreen = false;
+  if (!(_functions & Func_Maximize) && (_max_horz || _max_vert))
+    if (frame) maximize(false, 0);
+    else _max_vert = _max_horz = false;
 }
 
+void Client::remaximize()
+{
+  int dir;
+  if (_max_horz && _max_vert)
+    dir = 0;
+  else if (_max_horz)
+    dir = 1;
+  else if (_max_vert)
+    dir = 2;
+  else
+    return; // not maximized
+  _max_horz = _max_vert = false;
+  maximize(true, dir, false);
+}
 
 void Client::applyStartupState()
 {
@@ -1205,11 +1413,11 @@ void Client::applyStartupState()
 
   if (_iconic) {
     _iconic = false;
-    setDesktop(ICONIC_DESKTOP);
+    iconify(true);
   }
   if (_fullscreen) {
     _fullscreen = false;
-    fullscreen(true);
+    fullscreen(true, false);
   }
   if (_shaded) {
     _shaded = false;
@@ -1218,8 +1426,16 @@ void Client::applyStartupState()
   if (_urgent)
     fireUrgent();
   
-  if (_max_vert); // XXX: incomplete
-  if (_max_horz); // XXX: incomplete
+  if (_max_vert && _max_horz) {
+    _max_vert = _max_horz = false;
+    maximize(true, 0, false);
+  } else if (_max_vert) {
+    _max_vert = false;
+    maximize(true, 2, false);
+  } else if (_max_horz) {
+    _max_horz = false;
+    maximize(true, 1, false);
+  }
 
   if (_skip_taskbar); // nothing to do for this
   if (_skip_pager);   // nothing to do for this
@@ -1228,7 +1444,6 @@ void Client::applyStartupState()
   if (_below);        // nothing to do for this
 }
 
-
 void Client::fireUrgent()
 {
   // call the python UrgentWindow callbacks
@@ -1236,7 +1451,6 @@ void Client::fireUrgent()
   openbox->bindings()->fireEvent(&data);
 }
 
-
 void Client::shade(bool shade)
 {
   if (!(_functions & Func_Shade) || // can't
@@ -1250,13 +1464,116 @@ void Client::shade(bool shade)
   frame->adjustSize();
 }
 
+void Client::maximize(bool max, int dir, bool savearea)
+{
+  assert(dir == 0 || dir == 1 || dir == 2);
+  if (!(_functions & Func_Maximize)) return; // can't
+
+  // check if already done
+  if (max) {
+    if (dir == 0 && _max_horz && _max_vert) return;
+    if (dir == 1 && _max_horz) return;
+    if (dir == 2 && _max_vert) return;
+  } else {
+    if (dir == 0 && !_max_horz && !_max_vert) return;
+    if (dir == 1 && !_max_horz) return;
+    if (dir == 2 && !_max_vert) return;
+  }
+
+  const otk::Rect &a = openbox->screen(_screen)->area(_desktop);
+  int x = frame->area().x(), y = frame->area().y(),
+    w = _area.width(), h = _area.height();
+  
+  if (max) {
+    if (savearea) {
+      long dimensions[4];
+      long *readdim;
+      unsigned long n = 4;
+
+      dimensions[0] = x;
+      dimensions[1] = y;
+      dimensions[2] = w;
+      dimensions[3] = h;
+
+      // get the property off the window and use it for the dimentions we are
+      // already maxed on
+      if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
+                             otk::Property::atoms.cardinal, &n,
+                             (long unsigned**) &readdim)) {
+        if (n >= 4) {
+          if (_max_horz) {
+            dimensions[0] = readdim[0];
+            dimensions[2] = readdim[2];
+          }
+          if (_max_vert) {
+            dimensions[1] = readdim[1];
+            dimensions[3] = readdim[3];
+          }
+        }
+        delete readdim;
+      }
+      
+      otk::Property::set(_window, otk::Property::atoms.openbox_premax,
+                         otk::Property::atoms.cardinal,
+                         (long unsigned*)dimensions, 4);
+    }
+    if (dir == 0 || dir == 1) { // horz
+      x = a.x();
+      w = a.width();
+    }
+    if (dir == 0 || dir == 2) { // vert
+      y = a.y();
+      h = a.height() - frame->size().top - frame->size().bottom;
+    }
+  } else {
+    long *dimensions;
+    long unsigned n = 4;
+      
+    if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
+                           otk::Property::atoms.cardinal, &n,
+                           (long unsigned**) &dimensions)) {
+      if (n >= 4) {
+        if (dir == 0 || dir == 1) { // horz
+          x = (signed int)dimensions[0];
+          w = (signed int)dimensions[2];
+        }
+        if (dir == 0 || dir == 2) { // vert
+          y = (signed int)dimensions[1];
+          h = (signed int)dimensions[3];
+        }
+      }
+      delete dimensions;
+    } else {
+      // pick some fallbacks...
+      if (dir == 0 || dir == 1) { // horz
+        x = a.x() + a.width() / 4;
+        w = a.width() / 2;
+      }
+      if (dir == 0 || dir == 2) { // vert
+        y = a.y() + a.height() / 4;
+        h = a.height() / 2;
+      }
+    }
+  }
+
+  if (dir == 0 || dir == 1) // horz
+    _max_horz = max;
+  if (dir == 0 || dir == 2) // vert
+    _max_vert = max;
 
-void Client::fullscreen(bool fs)
+  if (!_max_horz && !_max_vert)
+    otk::Property::erase(_window, otk::Property::atoms.openbox_premax);
+
+  changeState(); // change the state hints on the client
+
+  frame->frameGravity(x, y); // figure out where the client should be going
+  internal_resize(TopLeft, w, h, true, x, y);
+}
+
+void Client::fullscreen(bool fs, bool savearea)
 {
   static FunctionFlags saved_func;
   static DecorationFlags saved_decor;
-  static otk::Rect saved_area;
-  static otk::Point saved_logical_size;
 
   if (!(_functions & Func_Fullscreen) || // can't
       _fullscreen == fs) return;         // already done
@@ -1264,6 +1581,8 @@ void Client::fullscreen(bool fs)
   _fullscreen = fs;
   changeState(); // change the state hints on the client
 
+  int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height();
+  
   if (fs) {
     // save the functions and remove them
     saved_func = _functions;
@@ -1271,25 +1590,52 @@ void Client::fullscreen(bool fs)
     // save the decorations and remove them
     saved_decor = _decorations;
     _decorations = 0;
-    // save the area and adjust it (we don't call internal resize here for
-    // constraints on the size, etc, we just make it fullscreen).
-    saved_area = _area;
+    if (savearea) {
+      long dimensions[4];
+      dimensions[0] = _area.x();
+      dimensions[1] = _area.y();
+      dimensions[2] = _area.width();
+      dimensions[3] = _area.height();
+      otk::Property::set(_window, otk::Property::atoms.openbox_premax,
+                         otk::Property::atoms.cardinal,
+                         (long unsigned*)dimensions, 4);
+    }
     const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
-    _area.setRect(0, 0, info->width(), info->height());
-    saved_logical_size = _logical_size;
-    _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(),
-                           (info->height() - _base_size.y()) / _size_inc.y());
+    x = 0;
+    y = 0;
+    w = info->size().width();
+    h = info->size().height();
   } else {
     _functions = saved_func;
     _decorations = saved_decor;
-    _area = saved_area;
-    _logical_size = saved_logical_size;
+
+    long *dimensions;
+    long unsigned n = 4;
+      
+    if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
+                           otk::Property::atoms.cardinal, &n,
+                           (long unsigned**) &dimensions)) {
+      if (n >= 4) {
+        x = dimensions[0];
+        y = dimensions[1];
+        w = dimensions[2];
+        h = dimensions[3];
+      }
+      delete dimensions;
+    } else {
+      // pick some fallbacks...
+      const otk::Rect &a = openbox->screen(_screen)->area(_desktop);
+      x = a.x() + a.width() / 4;
+      y = a.y() + a.height() / 4;
+      w = a.width() / 2;
+        h = a.height() / 2;
+    }    
   }
   
   changeAllowedActions();  // based on the new _functions
-  
-  frame->adjustSize();     // drop/replace the decor's and resize
-  frame->adjustPosition(); // get (back) in position!
+
+  // when fullscreening, don't obey things like increments, fill the screen
+  internal_resize(TopLeft, w, h, !fs, x, y);
 
   // raise (back) into our stacking layer
   openbox->screen(_screen)->raiseWindow(this);
@@ -1298,12 +1644,78 @@ void Client::fullscreen(bool fs)
   if (fs) focus();
 }
 
+void Client::iconify(bool iconic, bool curdesk)
+{
+  if (_iconic == iconic) return; // nothing to do
+
+#ifdef DEBUG
+    printf("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"), _window);
+#endif
+
+  _iconic = iconic;
+
+  if (_iconic) {
+    _wmstate = IconicState;
+    ignore_unmaps++;
+    // we unmap the client itself so that we can get MapRequest events, and
+    // because the ICCCM tells us to!
+    XUnmapWindow(**otk::display, _window);
+  } else {
+    if (curdesk)
+      setDesktop(openbox->screen(_screen)->desktop());
+    _wmstate = NormalState;
+    XMapWindow(**otk::display, _window);
+  }
+  changeState();
+  showhide();
+  openbox->screen(_screen)->updateStruts();
+}
+
+void Client::disableDecorations(DecorationFlags flags)
+{
+  _disabled_decorations = flags;
+  setupDecorAndFunctions();
+}
+
+void Client::installColormap(bool install) const
+{
+  XWindowAttributes wa;
+  if (XGetWindowAttributes(**otk::display, _window, &wa)) {
+    if (install)
+      XInstallColormap(**otk::display, wa.colormap);
+    else
+      XUninstallColormap(**otk::display, wa.colormap);
+  }
+}
+
+Client *Client::searchModalTree(Client *node, Client *skip)
+{
+  List::const_iterator it, end = node->_transients.end();
+  Client *ret;
+  
+  for (it = node->_transients.begin(); it != end; ++it) {
+    if (*it == skip) continue; // circular?
+    if ((ret = searchModalTree(*it, skip))) return ret; // got one
+    if ((*it)->_modal) return *it; // got one
+  }
+  return 0;
+}
+
+Client *Client::findModalChild()
+{
+  return searchModalTree(this, this);
+}
+
 
 bool Client::focus()
 {
+  // if we have a modal child, then focus it, not us
+  Client *c = findModalChild();
+  if (c) return c->focus();
+
   // won't try focus if the client doesn't want it, or if the window isn't
   // visible on the screen
-  if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
+  if (!(frame->visible() && (_can_focus || _focus_notify))) return false;
 
   if (_focused) return true;
 
@@ -1343,6 +1755,7 @@ bool Client::focus()
     XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
   }
 
+  XSync(**otk::display, False);
   return true;
 }
 
@@ -1364,8 +1777,10 @@ void Client::focusHandler(const XFocusChangeEvent &e)
   
   otk::EventHandler::focusHandler(e);
 
-  frame->focus();
   _focused = true;
+  frame->adjustFocus();
+
+  calcLayer(); // focus state can affect the stacking layer
 
   openbox->setFocusedClient(this);
 }
@@ -1379,21 +1794,45 @@ void Client::unfocusHandler(const XFocusChangeEvent &e)
   
   otk::EventHandler::unfocusHandler(e);
 
-  frame->unfocus();
   _focused = false;
+  frame->adjustFocus();
+
+  calcLayer(); // focus state can affect the stacking layer
 
   if (openbox->focusedClient() == this)
     openbox->setFocusedClient(0);
 }
 
 
-void Client::configureRequestHandler(const XConfigureRequestEvent &e)
+void Client::configureRequestHandler(const XConfigureRequestEvent &ec)
 {
 #ifdef    DEBUG
-  printf("ConfigureRequest for 0x%lx\n", e.window);
+  printf("ConfigureRequest for 0x%lx\n", ec.window);
 #endif // DEBUG
   
-  otk::EventHandler::configureRequestHandler(e);
+  otk::EventHandler::configureRequestHandler(ec);
+
+  // compress these
+  XConfigureRequestEvent e = ec;
+  XEvent ev;
+  while (XCheckTypedWindowEvent(**otk::display, window(), ConfigureRequest,
+                                &ev)) {
+    // XXX if this causes bad things.. we can compress config req's with the
+    //     same mask.
+    e.value_mask |= ev.xconfigurerequest.value_mask;
+    if (ev.xconfigurerequest.value_mask & CWX)
+      e.x = ev.xconfigurerequest.x;
+    if (ev.xconfigurerequest.value_mask & CWY)
+      e.y = ev.xconfigurerequest.y;
+    if (ev.xconfigurerequest.value_mask & CWWidth)
+      e.width = ev.xconfigurerequest.width;
+    if (ev.xconfigurerequest.value_mask & CWHeight)
+      e.height = ev.xconfigurerequest.height;
+    if (ev.xconfigurerequest.value_mask & CWBorderWidth)
+      e.border_width = ev.xconfigurerequest.border_width;
+    if (ev.xconfigurerequest.value_mask & CWStackMode)
+      e.detail = ev.xconfigurerequest.detail;
+  }
 
   // if we are iconic (or shaded (fvwm does this)) ignore the event
   if (_iconic || _shaded) return;
@@ -1524,7 +1963,7 @@ void Client::mapRequestHandler(const XMapRequestEvent &e)
   assert(_iconic); // we shouldn't be able to get this unless we're iconic
 
   // move to the current desktop (uniconify)
-  setDesktop(openbox->screen(_screen)->desktop());
+  iconify(false);
   // XXX: should we focus/raise the window? (basically a net_wm_active_window)
 }
 
This page took 0.052163 seconds and 4 git commands to generate.