]> Dogcows Code - chaz/openbox/blobdiff - src/client.cc
send configure notify when moving a window
[chaz/openbox] / src / client.cc
index 95d1509562d114b6a7861d10807a7973ac10e31c..f7207bbc85fae1bf67734dfdc9811d058228f1d7 100644 (file)
@@ -41,45 +41,18 @@ OBClient::OBClient(int screen, Window window)
   _decorations = _functions = 0;
   // start unfocused
   _focused = false;
+  // not a transient by default of course
+  _transient_for = 0;
   
   getArea();
   getDesktop();
-  // XXX: updateTransientFor();
-  getType();
-
-  // set the decorations and functions
-  switch (_type) {
-  case Type_Normal:
-    // normal windows retain all of the possible decorations and
-    // functionality
-    _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
-                   Decor_Iconify | Decor_Maximize;
-    _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
-
-  case Type_Dialog:
-    // dialogs cannot be maximized
-    _decorations &= ~Decor_Maximize;
-    _functions &= ~Func_Maximize;
-    break;
 
-  case Type_Menu:
-  case Type_Toolbar:
-  case Type_Utility:
-    // these windows get less functionality
-    _decorations &= ~(Decor_Iconify | Decor_Handle);
-    _functions &= ~(Func_Iconify | Func_Resize);
-    break;
+  updateTransientFor();
+  getType();
+  getMwmHints();
 
-  case Type_Desktop:
-  case Type_Dock:
-  case Type_Splash:
-    // none of these windows are manipulated by the window manager
-    _decorations = 0;
-    _functions = 0;
-    break;
-  }
+  setupDecorAndFunctions();
   
-  getMwmHints(); // this fucks (in good ways) with the decors and functions
   getState();
   getShaped();
 
@@ -89,8 +62,8 @@ OBClient::OBClient(int screen, Window window)
   updateTitle();
   updateIconTitle();
   updateClass();
+  updateStrut();
 
-  calcLayer();
   changeState();
 }
 
@@ -99,6 +72,10 @@ OBClient::~OBClient()
 {
   const otk::OBProperty *property = Openbox::instance->property();
 
+  // clean up parents reference to this
+  if (_transient_for)
+    _transient_for->_transients.remove(this); // remove from old parent
+  
   if (Openbox::instance->state() != Openbox::State_Exiting) {
     // these values should not be persisted across a window unmapping/mapping
     property->erase(_window, otk::OBProperty::net_wm_desktop);
@@ -112,11 +89,17 @@ void OBClient::getDesktop()
   const otk::OBProperty *property = Openbox::instance->property();
 
   // defaults to the current desktop
-  _desktop = 0; // XXX: change this to the current desktop!
-
-  property->get(_window, otk::OBProperty::net_wm_desktop,
-                otk::OBProperty::Atom_Cardinal,
-                &_desktop);
+  _desktop = Openbox::instance->screen(_screen)->desktop();
+
+  if (!property->get(_window, otk::OBProperty::net_wm_desktop,
+                     otk::OBProperty::Atom_Cardinal,
+                     (long unsigned*)&_desktop)) {
+    // make sure the hint exists
+    Openbox::instance->property()->set(_window,
+                                       otk::OBProperty::net_wm_desktop,
+                                       otk::OBProperty::Atom_Cardinal,
+                                       (unsigned)_desktop);
+  }
 }
 
 
@@ -161,6 +144,8 @@ void OBClient::getType()
 //               property->atom(otk::OBProperty::kde_net_wm_window_type_override))
 //        mwm_decorations = 0; // prevent this window from getting any decor
       // XXX: make this work again
+      if (_type != (WindowType) -1)
+        break; // grab the first known type
     }
     delete val;
   }
@@ -170,66 +155,107 @@ void OBClient::getType()
      * the window type hint was not set, which means we either classify ourself
      * as a normal window or a dialog, depending on if we are a transient.
      */
-    // XXX: make this code work!
-    //if (isTransient())
-    //  _type = Type_Dialog;
-    //else
+    if (_transient_for)
+      _type = Type_Dialog;
+    else
       _type = Type_Normal;
   }
 }
 
 
-void OBClient::getMwmHints()
+void OBClient::setupDecorAndFunctions()
 {
-  const otk::OBProperty *property = Openbox::instance->property();
+  // start with everything
+  _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
+    Decor_Iconify | Decor_Maximize;
+  _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
+  
+  switch (_type) {
+  case Type_Normal:
+    // normal windows retain all of the possible decorations and
+    // functionality
 
-  unsigned long num;
-  MwmHints *hints;
+  case Type_Dialog:
+    // dialogs cannot be maximized
+    _decorations &= ~Decor_Maximize;
+    _functions &= ~Func_Maximize;
+    break;
 
-  num = MwmHints::elements;
-  if (!property->get(_window, otk::OBProperty::motif_wm_hints,
-                     otk::OBProperty::motif_wm_hints, &num,
-                     (unsigned long **)&hints))
-    return;
-  
-  if (num < MwmHints::elements) {
-    delete [] hints;
-    return;
+  case Type_Menu:
+  case Type_Toolbar:
+  case Type_Utility:
+    // these windows get less functionality
+    _decorations &= ~(Decor_Iconify | Decor_Handle);
+    _functions &= ~(Func_Iconify | Func_Resize);
+    break;
+
+  case Type_Desktop:
+  case Type_Dock:
+  case Type_Splash:
+    // none of these windows are manipulated by the window manager
+    _decorations = 0;
+    _functions = 0;
+    break;
   }
 
-  // retrieved the hints
   // Mwm Hints are applied subtractively to what has already been chosen for
   // decor and functionality
-
-  if (hints->flags & MwmFlag_Decorations) {
-    if (! (hints->decorations & MwmDecor_All)) {
-      if (! (hints->decorations & MwmDecor_Border))
+  if (_mwmhints.flags & MwmFlag_Decorations) {
+    if (! (_mwmhints.decorations & MwmDecor_All)) {
+      if (! (_mwmhints.decorations & MwmDecor_Border))
         _decorations &= ~Decor_Border;
-      if (! (hints->decorations & MwmDecor_Handle))
+      if (! (_mwmhints.decorations & MwmDecor_Handle))
         _decorations &= ~Decor_Handle;
-      if (! (hints->decorations & MwmDecor_Title))
+      if (! (_mwmhints.decorations & MwmDecor_Title))
         _decorations &= ~Decor_Titlebar;
-      if (! (hints->decorations & MwmDecor_Iconify))
+      if (! (_mwmhints.decorations & MwmDecor_Iconify))
         _decorations &= ~Decor_Iconify;
-      if (! (hints->decorations & MwmDecor_Maximize))
+      if (! (_mwmhints.decorations & MwmDecor_Maximize))
         _decorations &= ~Decor_Maximize;
     }
   }
 
-  if (hints->flags & MwmFlag_Functions) {
-    if (! (hints->functions & MwmFunc_All)) {
-      if (! (hints->functions & MwmFunc_Resize))
+  if (_mwmhints.flags & MwmFlag_Functions) {
+    if (! (_mwmhints.functions & MwmFunc_All)) {
+      if (! (_mwmhints.functions & MwmFunc_Resize))
         _functions &= ~Func_Resize;
-      if (! (hints->functions & MwmFunc_Move))
+      if (! (_mwmhints.functions & MwmFunc_Move))
         _functions &= ~Func_Move;
-      if (! (hints->functions & MwmFunc_Iconify))
+      if (! (_mwmhints.functions & MwmFunc_Iconify))
         _functions &= ~Func_Iconify;
-      if (! (hints->functions & MwmFunc_Maximize))
+      if (! (_mwmhints.functions & MwmFunc_Maximize))
         _functions &= ~Func_Maximize;
-      //if (! (hints->functions & MwmFunc_Close))
+      // dont let mwm hints kill the close button
+      //if (! (_mwmhints.functions & MwmFunc_Close))
       //  _functions &= ~Func_Close;
     }
   }
+
+  // XXX: changeAllowedActions();
+}
+
+
+void OBClient::getMwmHints()
+{
+  const otk::OBProperty *property = Openbox::instance->property();
+
+  unsigned long num = MwmHints::elements;
+  unsigned long *hints;
+
+  _mwmhints.flags = 0; // default to none
+  
+  if (!property->get(_window, otk::OBProperty::motif_wm_hints,
+                     otk::OBProperty::motif_wm_hints, &num,
+                     (unsigned long **)&hints))
+    return;
+  
+  if (num >= MwmHints::elements) {
+    // retrieved the hints
+    _mwmhints.flags = hints[0];
+    _mwmhints.functions = hints[1];
+    _mwmhints.decorations = hints[2];
+  }
+
   delete [] hints;
 }
 
@@ -263,9 +289,10 @@ void OBClient::getState()
       if (state[i] == property->atom(otk::OBProperty::net_wm_state_modal))
         _modal = true;
       else if (state[i] ==
-               property->atom(otk::OBProperty::net_wm_state_shaded))
+               property->atom(otk::OBProperty::net_wm_state_shaded)) {
         _shaded = true;
-      else if (state[i] ==
+        _wmstate = IconicState;
+      } else if (state[i] ==
                property->atom(otk::OBProperty::net_wm_state_skip_taskbar))
         _skip_taskbar = true;
       else if (state[i] ==
@@ -313,13 +340,29 @@ void OBClient::getShaped()
 
 
 void OBClient::calcLayer() {
-  if (_iconic) _layer = OBScreen::Layer_Icon;
-  else if (_type == Type_Desktop) _layer = OBScreen::Layer_Desktop;
-  else if (_type == Type_Dock) _layer = OBScreen::Layer_Top;
-  else if (_fullscreen) _layer = OBScreen::Layer_Fullscreen;
-  else if (_above) _layer = OBScreen::Layer_Above;
-  else if (_below) _layer = OBScreen::Layer_Below;
-  else _layer = OBScreen::Layer_Normal;
+  StackLayer l;
+
+  if (_iconic) l = Layer_Icon;
+  else if (_fullscreen) l = Layer_Fullscreen;
+  else if (_type == Type_Desktop) l = Layer_Desktop;
+  else if (_type == Type_Dock) {
+    if (!_below) l = Layer_Top;
+    else l = Layer_Normal;
+  }
+  else if (_above) l = Layer_Above;
+  else if (_below) l = Layer_Below;
+  else l = Layer_Normal;
+
+  if (l != _layer) {
+    _layer = l;
+    if (frame) {
+      /*
+        if we don't have a frame, then we aren't mapped yet (and this would
+        SIGSEGV :)
+      */
+      Openbox::instance->screen(_screen)->restack(true, this); // raise
+    }
+  }
 }
 
 
@@ -473,17 +516,81 @@ void OBClient::updateClass()
   const otk::OBProperty *property = Openbox::instance->property();
 
   // set the defaults
-  _app_name = _app_class = "";
+  _app_name = _app_class = _role = "";
 
   otk::OBProperty::StringVect v;
   unsigned long num = 2;
 
-  if (! property->get(_window, otk::OBProperty::wm_class,
-                      otk::OBProperty::ascii, &num, &v))
+  if (property->get(_window, otk::OBProperty::wm_class,
+                    otk::OBProperty::ascii, &num, &v)) {
+    if (num > 0) _app_name = v[0];
+    if (num > 1) _app_class = v[1];
+  }
+
+  v.clear();
+  num = 1;
+  if (property->get(_window, otk::OBProperty::wm_window_role,
+                    otk::OBProperty::ascii, &num, &v)) {
+    if (num > 0) _role = v[0];
+  }
+}
+
+
+void OBClient::updateStrut()
+{
+  unsigned long num = 4;
+  unsigned long *data;
+  if (!Openbox::instance->property()->get(_window,
+                                          otk::OBProperty::net_wm_strut,
+                                          otk::OBProperty::Atom_Cardinal,
+                                          &num, &data))
     return;
 
-  if (num > 0) _app_name = v[0];
-  if (num > 1) _app_class = v[1];
+  if (num == 4) {
+    _strut.left = data[0];
+    _strut.right = data[1];
+    _strut.top = data[2];
+    _strut.bottom = data[3];
+    
+    Openbox::instance->screen(_screen)->updateStrut();
+  }
+
+  delete [] data;
+}
+
+
+void OBClient::updateTransientFor()
+{
+  Window t = 0;
+  OBClient *c = 0;
+
+  if (XGetTransientForHint(otk::OBDisplay::display, _window, &t) &&
+      t != _window) { // cant be transient to itself!
+    c = Openbox::instance->findClient(t);
+    assert(c != this); // if this happens then we need to check for it
+
+    if (!c /*XXX: && _group*/) {
+      // not transient to a client, see if it is transient for a group
+      if (//t == _group->leader() ||
+        t == None ||
+        t == otk::OBDisplay::screenInfo(_screen)->rootWindow()) {
+        // window is a transient for its group!
+        // XXX: for now this is treated as non-transient.
+        //      this needs to be fixed!
+      }
+    }
+  }
+
+  // if anything has changed...
+  if (c != _transient_for) {
+    if (_transient_for)
+      _transient_for->_transients.remove(this); // remove from old parent
+    _transient_for = c;
+    if (_transient_for)
+      _transient_for->_transients.push_back(this); // add to new parent
+
+    // XXX: change decor status?
+  }
 }
 
 
@@ -508,6 +615,13 @@ void OBClient::propertyHandler(const XPropertyEvent &e)
     updateNormalHints();
   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 == property->atom(otk::OBProperty::net_wm_name) ||
            e.atom == property->atom(otk::OBProperty::wm_name))
     updateTitle();
@@ -518,8 +632,8 @@ void OBClient::propertyHandler(const XPropertyEvent &e)
     updateClass();
   else if (e.atom == property->atom(otk::OBProperty::wm_protocols))
     updateProtocols();
-  // XXX: transient for hint
-  // XXX: strut hint
+  else if (e.atom == property->atom(otk::OBProperty::net_wm_strut))
+    updateStrut();
 }
 
 
@@ -527,7 +641,8 @@ void OBClient::setWMState(long state)
 {
   if (state == _wmstate) return; // no change
   
-  switch (state) {
+  _wmstate = state;
+  switch (_wmstate) {
   case IconicState:
     // XXX: cause it to iconify
     break;
@@ -535,25 +650,37 @@ void OBClient::setWMState(long state)
     // XXX: cause it to uniconify
     break;
   }
-  _wmstate = state;
 }
 
 
 void OBClient::setDesktop(long target)
 {
+  if (target == _desktop) return;
+  
   printf("Setting desktop %ld\n", target);
-  assert(target >= 0 || target == (signed)0xffffffff);
-  //assert(target == 0xffffffff || target < MAX);
 
-  // XXX: move the window to the new desktop (and set root property)
+  if (!(target >= 0 || target == (signed)0xffffffff)) return;
+  
   _desktop = target;
+
+  Openbox::instance->property()->set(_window,
+                                     otk::OBProperty::net_wm_desktop,
+                                     otk::OBProperty::Atom_Cardinal,
+                                     (unsigned)_desktop);
+  
+  // 'move' the window to the new desktop
+  if (_desktop == Openbox::instance->screen(_screen)->desktop() ||
+      _desktop == (signed)0xffffffff)
+    frame->show();
+  else
+    frame->hide();
 }
 
 
 void OBClient::setState(StateAction action, long data1, long data2)
 {
   const otk::OBProperty *property = Openbox::instance->property();
-  bool restack = false, shadestate = _shaded;
+  bool shadestate = _shaded;
 
   if (!(action == State_Add || action == State_Remove ||
         action == State_Toggle))
@@ -621,17 +748,14 @@ void OBClient::setState(StateAction action, long data1, long data2)
                  property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
         if (_fullscreen) continue;
         _fullscreen = true;
-        restack = false;
       } else if (state ==
                  property->atom(otk::OBProperty::net_wm_state_above)) {
         if (_above) continue;
         _above = true;
-        restack = true;
       } else if (state ==
                  property->atom(otk::OBProperty::net_wm_state_below)) {
         if (_below) continue;
         _below = true;
-        restack = true;
       }
 
     } else { // action == State_Remove
@@ -663,26 +787,20 @@ void OBClient::setState(StateAction action, long data1, long data2)
                  property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
         if (!_fullscreen) continue;
         _fullscreen = false;
-        restack = true;
       } else if (state ==
                  property->atom(otk::OBProperty::net_wm_state_above)) {
         if (!_above) continue;
         _above = false;
-        restack = true;
       } else if (state ==
                  property->atom(otk::OBProperty::net_wm_state_below)) {
         if (!_below) continue;
         _below = false;
-        restack = true;
       }
     }
   }
   if (shadestate != _shaded)
     shade(shadestate);
-  if (restack) {
-    calcLayer();
-    Openbox::instance->screen(_screen)->restack(true, this); // raise
-  }
+  calcLayer();
 }
 
 
@@ -778,15 +896,29 @@ void OBClient::clientMessageHandler(const XClientMessageEvent &e)
       setDesktop(e.data.l[0]); // use the original event
   } else if (e.message_type == property->atom(otk::OBProperty::net_wm_state)) {
     // can't compress these
+#ifdef DEBUG
+    printf("net_wm_state %s %ld %ld for 0x%lx\n",
+           (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
+            e.data.l[0] == 2 ? "Toggle" : "INVALID"),
+           e.data.l[1], e.data.l[2], _window);
+#endif
     setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
   } else if (e.message_type ==
              property->atom(otk::OBProperty::net_close_window)) {
+#ifdef DEBUG
+    printf("net_close_window for 0x%lx\n", _window);
+#endif
     close();
   } else if (e.message_type ==
              property->atom(otk::OBProperty::net_active_window)) {
+#ifdef DEBUG
+    printf("net_active_window for 0x%lx\n", _window);
+#endif
+    if (_shaded)
+      shade(false);
+    // XXX: deiconify
     focus();
     Openbox::instance->screen(_screen)->restack(true, this); // raise
-  } else {
   }
 }
 
@@ -795,9 +927,11 @@ void OBClient::clientMessageHandler(const XClientMessageEvent &e)
 void OBClient::shapeHandler(const XShapeEvent &e)
 {
   otk::OtkEventHandler::shapeHandler(e);
-  
-  _shaped = e.shaped;
-  frame->adjustShape();
+
+  if (e.kind == ShapeBounding) {
+    _shaped = e.shaped;
+    frame->adjustShape();
+  }
 }
 #endif
 
@@ -869,7 +1003,24 @@ void OBClient::move(int x, int y)
   _area.setPos(x, y);
 
   // move the frame to be in the requested position
-  frame->adjustPosition();
+  if (frame) // this can be called while mapping, before frame exists
+    frame->adjustPosition();
+
+  // send synthetic configure notify
+  XEvent event;
+  event.type = ConfigureNotify;
+  event.xconfigure.display = otk::OBDisplay::display;
+  event.xconfigure.event = _window;
+  event.xconfigure.window = _window;
+  event.xconfigure.x = x;
+  event.xconfigure.y = y;
+  event.xconfigure.width = _area.width();
+  event.xconfigure.height = _area.height();
+  event.xconfigure.border_width = _border_width;
+  event.xconfigure.above = frame->window();
+  event.xconfigure.override_redirect = False;
+  XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
+             StructureNotifyMask, &event);
 }
 
 
@@ -896,7 +1047,7 @@ void OBClient::close()
   ce.xclient.data.l[2] = 0l;
   ce.xclient.data.l[3] = 0l;
   ce.xclient.data.l[4] = 0l;
-  XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
+  XSendEvent(otk::OBDisplay::display, _window, false, NoEventMask, &ce);
 }
 
 
@@ -937,9 +1088,11 @@ void OBClient::changeState()
     netstate[num++] = property->atom(otk::OBProperty::net_wm_state_below);
   property->set(_window, otk::OBProperty::net_wm_state,
                 otk::OBProperty::Atom_Atom, netstate, num);
-  
+
+  calcLayer();
 }
 
+
 void OBClient::shade(bool shade)
 {
   if (shade == _shaded) return; // already done
@@ -951,12 +1104,17 @@ void OBClient::shade(bool shade)
 }
 
 
-bool OBClient::focus()
+bool OBClient::focus() const
 {
-  if (!(_can_focus || _focus_notify) || _focused) return false;
+  // 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 (_focused) return true;
 
   if (_can_focus)
-    XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
+    XSetInputFocus(otk::OBDisplay::display, _window,
+                   RevertToNone, CurrentTime);
 
   if (_focus_notify) {
     XEvent ce;
@@ -979,7 +1137,7 @@ bool OBClient::focus()
 }
 
 
-void OBClient::unfocus()
+void OBClient::unfocus() const
 {
   if (!_focused) return;
 
@@ -991,7 +1149,7 @@ void OBClient::unfocus()
 void OBClient::focusHandler(const XFocusChangeEvent &e)
 {
 #ifdef    DEBUG
-  printf("FocusIn for 0x%lx\n", e.window);
+//  printf("FocusIn for 0x%lx\n", e.window);
 #endif // DEBUG
   
   OtkEventHandler::focusHandler(e);
@@ -1006,7 +1164,7 @@ void OBClient::focusHandler(const XFocusChangeEvent &e)
 void OBClient::unfocusHandler(const XFocusChangeEvent &e)
 {
 #ifdef    DEBUG
-  printf("FocusOut for 0x%lx\n", e.window);
+//  printf("FocusOut for 0x%lx\n", e.window);
 #endif // DEBUG
   
   OtkEventHandler::unfocusHandler(e);
@@ -1014,10 +1172,8 @@ void OBClient::unfocusHandler(const XFocusChangeEvent &e)
   frame->unfocus();
   _focused = false;
 
-  if (Openbox::instance->focusedClient() == this) {
-    printf("UNFOCUSED!\n");
-    Openbox::instance->setFocusedClient(this);
-  }
+  if (Openbox::instance->focusedClient() == this)
+    Openbox::instance->setFocusedClient(0);
 }
 
 
@@ -1073,13 +1229,13 @@ void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
     switch (e.detail) {
     case Below:
     case BottomIf:
-      // XXX: lower the window
+      Openbox::instance->screen(_screen)->restack(false, this); // lower
       break;
 
     case Above:
     case TopIf:
     default:
-      // XXX: raise the window
+      Openbox::instance->screen(_screen)->restack(true, this); // raise
       break;
     }
   }
@@ -1088,15 +1244,18 @@ void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
 
 void OBClient::unmapHandler(const XUnmapEvent &e)
 {
+  if (ignore_unmaps) {
 #ifdef    DEBUG
-  printf("UnmapNotify for 0x%lx\n", e.window);
+    printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
 #endif // DEBUG
-
-  if (ignore_unmaps) {
     ignore_unmaps--;
     return;
   }
   
+#ifdef    DEBUG
+  printf("UnmapNotify for 0x%lx\n", e.window);
+#endif // DEBUG
+
   OtkEventHandler::unmapHandler(e);
 
   // this deletes us etc
@@ -1135,6 +1294,12 @@ void OBClient::reparentHandler(const XReparentEvent &e)
     to an already unmapped window.
   */
 
+  // we don't want the reparent event, put it back on the stack for the X
+  // server to deal with after we unmanage the window
+  XEvent ev;
+  ev.xreparent = e;
+  XPutBackEvent(otk::OBDisplay::display, &ev);
+  
   // this deletes us etc
   Openbox::instance->screen(_screen)->unmanageWindow(this);
 }
This page took 0.035461 seconds and 4 git commands to generate.