]> Dogcows Code - chaz/openbox/blobdiff - src/client.cc
no more configuration.hh
[chaz/openbox] / src / client.cc
index eeb09a374981a4fe5cfe9dc30d3f6386c6faa980..f60a8da62e33882c38631cd62f3d9751ae5f6e0f 100644 (file)
@@ -23,6 +23,8 @@ extern "C" {
 #define _(str) gettext(str)
 }
 
+#include <algorithm>
+
 namespace ob {
 
 Client::Client(int screen, Window window)
@@ -47,6 +49,10 @@ Client::Client(int screen, Window window)
   _layer = Layer_Normal;
   // default to not urgent
   _urgent = false;
+  // not positioned unless specified
+  _positioned = false;
+  // nothing is disabled unless specified
+  _disabled_decorations = 0;
   
   getArea();
   getDesktop();
@@ -60,12 +66,14 @@ Client::Client(int screen, Window window)
 
   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
@@ -113,6 +121,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;
@@ -198,12 +221,18 @@ void Client::setupDecorAndFunctions()
     _decorations |= Decor_Close;
     _functions |= Func_Close;
   }
+
+  if (!(_min_size.x() < _max_size.x() || _min_size.y() < _max_size.y())) {
+    _decorations &= ~(Decor_Maximize | Decor_Handle);
+    _functions &= ~(Func_Resize | Func_Maximize);
+  }
   
   switch (_type) {
   case Type_Normal:
     // 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
@@ -264,7 +293,34 @@ void Client::setupDecorAndFunctions()
     }
   }
 
+  // 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;
+
+  // You can't shade without a titlebar
+  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
+  }
 }
 
 
@@ -418,6 +474,8 @@ void Client::updateNormalHints()
   int oldgravity = _gravity;
 
   // defaults
+  _min_ratio = 0.0;
+  _max_ratio = 0.0;
   _size_inc.setPoint(1, 1);
   _base_size.setPoint(0, 0);
   _min_size.setPoint(0, 0);
@@ -443,6 +501,11 @@ void Client::updateNormalHints()
       }
     }
 
+    if (size.flags & PAspect) {
+      if (size.min_aspect.y) _min_ratio = size.min_aspect.x/size.min_aspect.y;
+      if (size.max_aspect.y) _max_ratio = size.max_aspect.x/size.max_aspect.y;
+    }
+
     if (size.flags & PMinSize)
       _min_size.setPoint(size.min_width, size.min_height);
     
@@ -497,7 +560,7 @@ void Client::updateWMHints(bool initstate)
 #endif
     // fire the urgent callback if we're mapped, otherwise, wait until after
     // we're mapped
-    if (_urgent && frame)
+    if (frame)
       fireUrgent();
   }
 }
@@ -576,8 +639,11 @@ 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)->updateStrut();
   }
 
   delete [] data;
@@ -622,6 +688,9 @@ void Client::updateTransientFor()
 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;
@@ -634,16 +703,16 @@ void Client::propertyHandler(const XPropertyEvent &e)
     }
   }
 
-  if (e.atom == XA_WM_NORMAL_HINTS)
+  if (e.atom == XA_WM_NORMAL_HINTS) {
     updateNormalHints();
-  else if (e.atom == XA_WM_HINTS)
+    setupDecorAndFunctions(); // normal hints can make a window non-resizable
+  } else if (e.atom == XA_WM_HINTS)
     updateWMHints();
   else if (e.atom == XA_WM_TRANSIENT_FOR) {
     updateTransientFor();
     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)
@@ -656,7 +725,6 @@ 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();
@@ -729,6 +797,8 @@ 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))
@@ -767,12 +837,10 @@ void Client::setState(StateAction action, long data1, long data2)
         _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;
+        maxh = true;
         // XXX: resize the window etc
       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
         shadestate = true;
@@ -795,13 +863,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) {
@@ -819,13 +883,29 @@ 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
 }
 
 
@@ -836,7 +916,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:
@@ -885,7 +966,8 @@ void Client::toggleClientBorder(bool 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);
 }
@@ -895,6 +977,9 @@ 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) {
@@ -980,24 +1065,39 @@ void Client::resize(Corner anchor, int w, int h)
 }
 
 
-void Client::internal_resize(Corner anchor, int w, int h, 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;
+  if (user) {
+    // for interactive resizing. have to move half an increment in each
+    // direction.
+    int mw = w % _size_inc.x(); // how far we are towards the next size inc
+    int mh = h % _size_inc.y();
+    int aw = _size_inc.x() / 2; // amount to add
+    int ah = _size_inc.y() / 2;
+    // don't let us move into a new size increment
+    if (mw + aw >= _size_inc.x()) aw = _size_inc.x() - mw - 1;
+    if (mh + ah >= _size_inc.y()) ah = _size_inc.y() - mh - 1;
+    w += aw;
+    h += ah;
+    
+    // if this is a user-requested resize, then check against min/max sizes
+    // and aspect ratios
 
-  // is the window resizable? if it is not, then don't check its sizes, the
-  // client can do what it wants and the user can't change it anyhow
-  if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
     // 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();
+
+    // adjust the height ot match the width for the aspect ratios
+    if (_min_ratio)
+      if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
+    if (_max_ratio)
+      if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
   }
 
   // keep to the increments
@@ -1068,15 +1168,23 @@ 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
   }
 }
 
@@ -1114,7 +1222,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)
@@ -1176,6 +1284,22 @@ void Client::changeAllowedActions(void)
 }
 
 
+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()
 {
   // these are in a carefully crafted order..
@@ -1186,7 +1310,7 @@ void Client::applyStartupState()
   }
   if (_fullscreen) {
     _fullscreen = false;
-    fullscreen(true);
+    fullscreen(true, false);
   }
   if (_shaded) {
     _shaded = false;
@@ -1195,8 +1319,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,12 +1360,128 @@ void Client::shade(bool shade)
 }
 
 
-void Client::fullscreen(bool fs)
+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;
+  }
+
+  int g = _gravity;
+  
+  const otk::Rect &a = openbox->screen(_screen)->area();
+  int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height();
+  
+  if (max) {
+    // when maximizing, put the client where we want, NOT the frame!
+    _gravity = StaticGravity;
+    // adjust our idea of position based on StaticGravity, so we stay put
+    // unless asked
+    frame->frameGravity(x, y);
+
+    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() + frame->size().top;
+      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;
+      }
+    }
+    otk::Property::erase(_window, otk::Property::atoms.openbox_premax);
+  }
+
+  if (dir == 0 || dir == 1) // horz
+    _max_horz = max;
+  if (dir == 0 || dir == 2) // vert
+    _max_vert = max;
+  changeState(); // change the state hints on the client
+
+  internal_resize(TopLeft, w, h, true, x, y);
+  _gravity = g;
+  if (max) {
+    // because of my little gravity trick in here, we have to set the position
+    // of the client to what it really is
+    int x, y;
+    frame->frameGravity(x, y);
+    _area.setPos(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
@@ -1241,6 +1489,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;
@@ -1248,25 +1498,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->width();
+    h = info->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();
+      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);
@@ -1276,6 +1553,13 @@ void Client::fullscreen(bool fs)
 }
 
 
+void Client::disableDecorations(DecorationFlags flags)
+{
+  _disabled_decorations = flags;
+  setupDecorAndFunctions();
+}
+
+
 bool Client::focus()
 {
   // won't try focus if the client doesn't want it, or if the window isn't
@@ -1320,6 +1604,7 @@ bool Client::focus()
     XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
   }
 
+  XSync(**otk::display, False);
   return true;
 }
 
@@ -1404,9 +1689,9 @@ void Client::configureRequestHandler(const XConfigureRequestEvent &e)
     if (e.value_mask & (CWX | CWY)) {
       int x = (e.value_mask & CWX) ? e.x : _area.x();
       int y = (e.value_mask & CWY) ? e.y : _area.y();
-      internal_resize(corner, w, h, x, y);
+      internal_resize(corner, w, h, false, x, y);
     } else // if JUST resizing...
-      internal_resize(corner, w, h);
+      internal_resize(corner, w, h, false);
   } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
     int x = (e.value_mask & CWX) ? e.x : _area.x();
     int y = (e.value_mask & CWY) ? e.y : _area.y();
This page took 0.035996 seconds and 4 git commands to generate.