Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

/src/client.cc

Go to the documentation of this file.
00001 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
00002 
00003 #ifdef HAVE_CONFIG_H
00004 # include "../config.h"
00005 #endif
00006 
00007 #include "client.hh"
00008 #include "frame.hh"
00009 #include "screen.hh"
00010 #include "openbox.hh"
00011 #include "bindings.hh"
00012 #include "otk/display.hh"
00013 #include "otk/property.hh"
00014 
00015 extern "C" {
00016 #include <X11/Xlib.h>
00017 #include <X11/Xutil.h>
00018 #include <X11/Xatom.h>
00019 
00020 #include <assert.h>
00021 
00022 #include "gettext.h"
00023 #define _(str) gettext(str)
00024 }
00025 
00026 #include <algorithm>
00027 
00028 namespace ob {
00029 
00030 Client::Client(int screen, Window window)
00031   : otk::EventHandler(),
00032     WidgetBase(WidgetBase::Type_Client),
00033     frame(0), _screen(screen), _window(window)
00034 {
00035   assert(screen >= 0);
00036   assert(window);
00037 
00038   ignore_unmaps = 0;
00039   
00040   // update EVERYTHING the first time!!
00041 
00042   // defaults
00043   _wmstate = NormalState;
00044   _focused = false;
00045   _transient_for = 0;
00046   _layer = Layer_Normal;
00047   _urgent = false;
00048   _positioned = false;
00049   _disabled_decorations = 0;
00050   _modal_child = 0;
00051   _group = None;
00052   _desktop = 0;
00053   
00054   getArea();
00055   getDesktop();
00056   getState();  // do this before updateTransientFor! (for _modal)
00057   getShaped();
00058 
00059   updateTransientFor();
00060   getMwmHints();
00061   getType(); // this can change the mwmhints for special cases
00062 
00063   updateProtocols();
00064 
00065   getGravity();        // get the attribute gravity
00066   updateNormalHints(); // this may override the attribute gravity
00067 
00068   // got the type, the mwmhints, the protocols, and the normal hints (min/max
00069   // sizes), so we're ready to set up
00070   // the decorations/functions
00071   setupDecorAndFunctions();
00072   
00073   // also get the initial_state and set _iconic if we aren't "starting"
00074   // when we're "starting" that means we should use whatever state was already
00075   // on the window over the initial map state, because it was already mapped
00076   updateWMHints(openbox->state() != Openbox::State_Starting);
00077   updateTitle();
00078   updateIconTitle();
00079   updateClass();
00080   updateStrut();
00081 
00082   // this makes sure that these windows appear on all desktops
00083   if (/*_type == Type_Dock ||*/ _type == Type_Desktop)
00084     _desktop = 0xffffffff;
00085   
00086   // set the desktop hint, to make sure that it always exists, and to reflect
00087   // any changes we've made here
00088   otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
00089                      otk::Property::atoms.cardinal, (unsigned)_desktop);
00090   
00091   changeState();
00092 }
00093 
00094 
00095 Client::~Client()
00096 {
00097   // clean up childrens' references
00098   while (!_transients.empty()) {
00099     _transients.front()->_transient_for = 0;
00100     _transients.pop_front();
00101   }
00102 
00103   // clean up parents reference to this
00104   if (_transient_for)
00105     _transient_for->_transients.remove(this); // remove from old parent
00106   
00107   if (openbox->state() != Openbox::State_Exiting) {
00108     // these values should not be persisted across a window unmapping/mapping
00109     otk::Property::erase(_window, otk::Property::atoms.net_wm_desktop);
00110     otk::Property::erase(_window, otk::Property::atoms.net_wm_state);
00111   } else {
00112     // if we're left in an iconic state, the client wont be mapped. this is
00113     // bad, since we will no longer be managing the window on restart
00114     if (_iconic)
00115       XMapWindow(**otk::display, _window);
00116   }
00117 }
00118 
00119 
00120 bool Client::validate() const
00121 {
00122   XSync(**otk::display, false); // get all events on the server
00123 
00124   XEvent e;
00125   if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &e) ||
00126       XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &e)) {
00127     XPutBackEvent(**otk::display, &e);
00128     return false;
00129   }
00130 
00131   return true;
00132 }
00133 
00134 
00135 void Client::getGravity()
00136 {
00137   XWindowAttributes wattrib;
00138   Status ret;
00139 
00140   ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
00141   assert(ret != BadWindow);
00142   _gravity = wattrib.win_gravity;
00143 }
00144 
00145 
00146 void Client::getDesktop()
00147 {
00148   // defaults to the current desktop
00149   _desktop = openbox->screen(_screen)->desktop();
00150 
00151   if (otk::Property::get(_window, otk::Property::atoms.net_wm_desktop,
00152                          otk::Property::atoms.cardinal,
00153                          (long unsigned*)&_desktop)) {
00154 #ifdef DEBUG
00155 //    printf("Window requested desktop: %ld\n", _desktop);
00156 #endif
00157   }
00158 }
00159 
00160 
00161 void Client::getType()
00162 {
00163   _type = (WindowType) -1;
00164   
00165   unsigned long *val;
00166   unsigned long num = (unsigned) -1;
00167   if (otk::Property::get(_window, otk::Property::atoms.net_wm_window_type,
00168                          otk::Property::atoms.atom, &num, &val)) {
00169     // use the first value that we know about in the array
00170     for (unsigned long i = 0; i < num; ++i) {
00171       if (val[i] == otk::Property::atoms.net_wm_window_type_desktop)
00172         _type = Type_Desktop;
00173       else if (val[i] == otk::Property::atoms.net_wm_window_type_dock)
00174         _type = Type_Dock;
00175       else if (val[i] == otk::Property::atoms.net_wm_window_type_toolbar)
00176         _type = Type_Toolbar;
00177       else if (val[i] == otk::Property::atoms.net_wm_window_type_menu)
00178         _type = Type_Menu;
00179       else if (val[i] == otk::Property::atoms.net_wm_window_type_utility)
00180         _type = Type_Utility;
00181       else if (val[i] == otk::Property::atoms.net_wm_window_type_splash)
00182         _type = Type_Splash;
00183       else if (val[i] == otk::Property::atoms.net_wm_window_type_dialog)
00184         _type = Type_Dialog;
00185       else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
00186         _type = Type_Normal;
00187       else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override){
00188         // prevent this window from getting any decor or functionality
00189         _mwmhints.flags &= MwmFlag_Functions | MwmFlag_Decorations;
00190         _mwmhints.decorations = 0;
00191         _mwmhints.functions = 0;
00192       }
00193       if (_type != (WindowType) -1)
00194         break; // grab the first known type
00195     }
00196     delete val;
00197   }
00198     
00199   if (_type == (WindowType) -1) {
00200     /*
00201      * the window type hint was not set, which means we either classify ourself
00202      * as a normal window or a dialog, depending on if we are a transient.
00203      */
00204     if (_transient_for)
00205       _type = Type_Dialog;
00206     else
00207       _type = Type_Normal;
00208   }
00209 }
00210 
00211 
00212 void Client::setupDecorAndFunctions()
00213 {
00214   // start with everything (cept fullscreen)
00215   _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
00216     Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
00217   _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
00218     Func_Shade;
00219   if (_delete_window) {
00220     _decorations |= Decor_Close;
00221     _functions |= Func_Close;
00222   }
00223 
00224   if (!(_min_size.x() < _max_size.x() || _min_size.y() < _max_size.y())) {
00225     _decorations &= ~(Decor_Maximize | Decor_Handle);
00226     _functions &= ~(Func_Resize | Func_Maximize);
00227   }
00228   
00229   switch (_type) {
00230   case Type_Normal:
00231     // normal windows retain all of the possible decorations and
00232     // functionality, and are the only windows that you can fullscreen
00233     _functions |= Func_Fullscreen;
00234     break;
00235 
00236   case Type_Dialog:
00237     // dialogs cannot be maximized
00238     _decorations &= ~Decor_Maximize;
00239     _functions &= ~Func_Maximize;
00240     break;
00241 
00242   case Type_Menu:
00243   case Type_Toolbar:
00244   case Type_Utility:
00245     // these windows get less functionality
00246     _decorations &= ~(Decor_Iconify | Decor_Handle);
00247     _functions &= ~(Func_Iconify | Func_Resize);
00248     break;
00249 
00250   case Type_Desktop:
00251   case Type_Dock:
00252   case Type_Splash:
00253     // none of these windows are manipulated by the window manager
00254     _decorations = 0;
00255     _functions = 0;
00256     break;
00257   }
00258 
00259   // Mwm Hints are applied subtractively to what has already been chosen for
00260   // decor and functionality
00261   if (_mwmhints.flags & MwmFlag_Decorations) {
00262     if (! (_mwmhints.decorations & MwmDecor_All)) {
00263       if (! (_mwmhints.decorations & MwmDecor_Border))
00264         _decorations &= ~Decor_Border;
00265       if (! (_mwmhints.decorations & MwmDecor_Handle))
00266         _decorations &= ~Decor_Handle;
00267       if (! (_mwmhints.decorations & MwmDecor_Title)) {
00268         _decorations &= ~Decor_Titlebar;
00269         // if we don't have a titlebar, then we cannot shade!
00270         _functions &= ~Func_Shade;
00271       }
00272       if (! (_mwmhints.decorations & MwmDecor_Iconify))
00273         _decorations &= ~Decor_Iconify;
00274       if (! (_mwmhints.decorations & MwmDecor_Maximize))
00275         _decorations &= ~Decor_Maximize;
00276     }
00277   }
00278 
00279   if (_mwmhints.flags & MwmFlag_Functions) {
00280     if (! (_mwmhints.functions & MwmFunc_All)) {
00281       if (! (_mwmhints.functions & MwmFunc_Resize))
00282         _functions &= ~Func_Resize;
00283       if (! (_mwmhints.functions & MwmFunc_Move))
00284         _functions &= ~Func_Move;
00285       if (! (_mwmhints.functions & MwmFunc_Iconify))
00286         _functions &= ~Func_Iconify;
00287       if (! (_mwmhints.functions & MwmFunc_Maximize))
00288         _functions &= ~Func_Maximize;
00289       // dont let mwm hints kill the close button
00290       //if (! (_mwmhints.functions & MwmFunc_Close))
00291       //  _functions &= ~Func_Close;
00292     }
00293   }
00294 
00295   // can't maximize without moving/resizing
00296   if (!((_functions & Func_Move) && (_functions & Func_Resize)))
00297     _functions &= ~Func_Maximize;
00298 
00299   // finally, user specified disabled decorations are applied to subtract
00300   // decorations
00301   if (_disabled_decorations & Decor_Titlebar)
00302     _decorations &= ~Decor_Titlebar;
00303   if (_disabled_decorations & Decor_Handle)
00304     _decorations &= ~Decor_Handle;
00305   if (_disabled_decorations & Decor_Border)
00306     _decorations &= ~Decor_Border;
00307   if (_disabled_decorations & Decor_Iconify)
00308     _decorations &= ~Decor_Iconify;
00309   if (_disabled_decorations & Decor_Maximize)
00310     _decorations &= ~Decor_Maximize;
00311   if (_disabled_decorations & Decor_AllDesktops)
00312     _decorations &= ~Decor_AllDesktops;
00313   if (_disabled_decorations & Decor_Close)
00314     _decorations &= ~Decor_Close;
00315 
00316   // You can't shade without a titlebar
00317   if (!(_decorations & Decor_Titlebar))
00318     _functions &= ~Func_Shade;
00319   
00320   changeAllowedActions();
00321 
00322   if (frame) {
00323     frame->adjustSize(); // change the decors on the frame
00324     frame->adjustPosition(); // with more/less decorations, we may need to be
00325                              // moved
00326   }
00327 }
00328 
00329 
00330 void Client::getMwmHints()
00331 {
00332   unsigned long num = MwmHints::elements;
00333   unsigned long *hints;
00334 
00335   _mwmhints.flags = 0; // default to none
00336   
00337   if (!otk::Property::get(_window, otk::Property::atoms.motif_wm_hints,
00338                           otk::Property::atoms.motif_wm_hints, &num,
00339                           (unsigned long **)&hints))
00340     return;
00341   
00342   if (num >= MwmHints::elements) {
00343     // retrieved the hints
00344     _mwmhints.flags = hints[0];
00345     _mwmhints.functions = hints[1];
00346     _mwmhints.decorations = hints[2];
00347   }
00348 
00349   delete [] hints;
00350 }
00351 
00352 
00353 void Client::getArea()
00354 {
00355   XWindowAttributes wattrib;
00356   Status ret;
00357   
00358   ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
00359   assert(ret != BadWindow);
00360 
00361   _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
00362   _border_width = wattrib.border_width;
00363 }
00364 
00365 
00366 void Client::getState()
00367 {
00368   _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
00369     _iconic = _skip_taskbar = _skip_pager = false;
00370   
00371   unsigned long *state;
00372   unsigned long num = (unsigned) -1;
00373   
00374   if (otk::Property::get(_window, otk::Property::atoms.net_wm_state,
00375                          otk::Property::atoms.atom, &num, &state)) {
00376     for (unsigned long i = 0; i < num; ++i) {
00377       if (state[i] == otk::Property::atoms.net_wm_state_modal)
00378         _modal = true;
00379       else if (state[i] == otk::Property::atoms.net_wm_state_shaded)
00380         _shaded = true;
00381       else if (state[i] == otk::Property::atoms.net_wm_state_hidden)
00382         _iconic = true;
00383       else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar)
00384         _skip_taskbar = true;
00385       else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager)
00386         _skip_pager = true;
00387       else if (state[i] == otk::Property::atoms.net_wm_state_fullscreen)
00388         _fullscreen = true;
00389       else if (state[i] == otk::Property::atoms.net_wm_state_maximized_vert)
00390         _max_vert = true;
00391       else if (state[i] == otk::Property::atoms.net_wm_state_maximized_horz)
00392         _max_horz = true;
00393       else if (state[i] == otk::Property::atoms.net_wm_state_above)
00394         _above = true;
00395       else if (state[i] == otk::Property::atoms.net_wm_state_below)
00396         _below = true;
00397     }
00398 
00399     delete [] state;
00400   }
00401 }
00402 
00403 
00404 void Client::getShaped()
00405 {
00406   _shaped = false;
00407 #ifdef   SHAPE
00408   if (otk::display->shape()) {
00409     int foo;
00410     unsigned int ufoo;
00411     int s;
00412 
00413     XShapeSelectInput(**otk::display, _window, ShapeNotifyMask);
00414 
00415     XShapeQueryExtents(**otk::display, _window, &s, &foo,
00416                        &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
00417     _shaped = (s != 0);
00418   }
00419 #endif // SHAPE
00420 }
00421 
00422 
00423 void Client::calcLayer() {
00424   StackLayer l;
00425 
00426   if (_iconic) l = Layer_Icon;
00427   else if (_fullscreen) l = Layer_Fullscreen;
00428   else if (_type == Type_Desktop) l = Layer_Desktop;
00429   else if (_type == Type_Dock) {
00430     if (!_below) l = Layer_Top;
00431     else l = Layer_Normal;
00432   }
00433   else if (_above) l = Layer_Above;
00434   else if (_below) l = Layer_Below;
00435   else l = Layer_Normal;
00436 
00437   if (l != _layer) {
00438     _layer = l;
00439     if (frame) {
00440       /*
00441         if we don't have a frame, then we aren't mapped yet (and this would
00442         SIGSEGV :)
00443       */
00444       openbox->screen(_screen)->raiseWindow(this);
00445     }
00446   }
00447 }
00448 
00449 
00450 void Client::updateProtocols()
00451 {
00452   Atom *proto;
00453   int num_return = 0;
00454 
00455   _focus_notify = false;
00456   _delete_window = false;
00457 
00458   if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) {
00459     for (int i = 0; i < num_return; ++i) {
00460       if (proto[i] == otk::Property::atoms.wm_delete_window) {
00461         // this means we can request the window to close
00462         _delete_window = true;
00463       } else if (proto[i] == otk::Property::atoms.wm_take_focus)
00464         // if this protocol is requested, then the window will be notified
00465         // by the window manager whenever it receives focus
00466         _focus_notify = true;
00467     }
00468     XFree(proto);
00469   }
00470 }
00471 
00472 
00473 void Client::updateNormalHints()
00474 {
00475   XSizeHints size;
00476   long ret;
00477   int oldgravity = _gravity;
00478 
00479   // defaults
00480   _min_ratio = 0.0;
00481   _max_ratio = 0.0;
00482   _size_inc.setPoint(1, 1);
00483   _base_size.setPoint(0, 0);
00484   _min_size.setPoint(0, 0);
00485   _max_size.setPoint(INT_MAX, INT_MAX);
00486 
00487   // get the hints from the window
00488   if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) {
00489     _positioned = (size.flags & (PPosition|USPosition));
00490 
00491     if (size.flags & PWinGravity) {
00492       _gravity = size.win_gravity;
00493       
00494       // if the client has a frame, i.e. has already been mapped and is
00495       // changing its gravity
00496       if (frame && _gravity != oldgravity) {
00497         // move our idea of the client's position based on its new gravity
00498         int x = frame->rect().x(), y = frame->rect().y();
00499         frame->frameGravity(x, y);
00500         _area.setPos(x, y);
00501       }
00502     }
00503 
00504     if (size.flags & PAspect) {
00505       if (size.min_aspect.y) _min_ratio = size.min_aspect.x/size.min_aspect.y;
00506       if (size.max_aspect.y) _max_ratio = size.max_aspect.x/size.max_aspect.y;
00507     }
00508 
00509     if (size.flags & PMinSize)
00510       _min_size.setPoint(size.min_width, size.min_height);
00511     
00512     if (size.flags & PMaxSize)
00513       _max_size.setPoint(size.max_width, size.max_height);
00514     
00515     if (size.flags & PBaseSize)
00516       _base_size.setPoint(size.base_width, size.base_height);
00517     
00518     if (size.flags & PResizeInc)
00519       _size_inc.setPoint(size.width_inc, size.height_inc);
00520   }
00521 }
00522 
00523 
00524 void Client::updateWMHints(bool initstate)
00525 {
00526   XWMHints *hints;
00527 
00528   // assume a window takes input if it doesnt specify
00529   _can_focus = true;
00530   bool ur = false;
00531   
00532   if ((hints = XGetWMHints(**otk::display, _window)) != NULL) {
00533     if (hints->flags & InputHint)
00534       _can_focus = hints->input;
00535 
00536     // only do this when initstate is true!
00537     if (initstate && (hints->flags & StateHint))
00538       _iconic = hints->initial_state == IconicState;
00539 
00540     if (hints->flags & XUrgencyHint)
00541       ur = true;
00542 
00543     if (hints->flags & WindowGroupHint) {
00544       if (hints->window_group != _group) {
00545         // XXX: remove from the old group if there was one
00546         _group = hints->window_group;
00547         // XXX: do stuff with the group
00548       }
00549     } else // no group!
00550       _group = None;
00551 
00552     XFree(hints);
00553   }
00554 
00555   if (ur != _urgent) {
00556     _urgent = ur;
00557 #ifdef DEBUG
00558     printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
00559            (long)_window, _urgent ? "ON" : "OFF");
00560 #endif
00561     // fire the urgent callback if we're mapped, otherwise, wait until after
00562     // we're mapped
00563     if (frame)
00564       fireUrgent();
00565   }
00566 }
00567 
00568 
00569 void Client::updateTitle()
00570 {
00571   _title = "";
00572   
00573   // try netwm
00574   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_name,
00575                           otk::Property::utf8, &_title)) {
00576     // try old x stuff
00577     otk::Property::get(_window, otk::Property::atoms.wm_name,
00578                        otk::Property::ascii, &_title);
00579   }
00580 
00581   if (_title.empty())
00582     _title = _("Unnamed Window");
00583 
00584   if (frame)
00585     frame->setTitle(_title);
00586 }
00587 
00588 
00589 void Client::updateIconTitle()
00590 {
00591   _icon_title = "";
00592   
00593   // try netwm
00594   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_icon_name,
00595                           otk::Property::utf8, &_icon_title)) {
00596     // try old x stuff
00597     otk::Property::get(_window, otk::Property::atoms.wm_icon_name,
00598                        otk::Property::ascii, &_icon_title);
00599   }
00600 
00601   if (_title.empty())
00602     _icon_title = _("Unnamed Window");
00603 }
00604 
00605 
00606 void Client::updateClass()
00607 {
00608   // set the defaults
00609   _app_name = _app_class = _role = "";
00610 
00611   otk::Property::StringVect v;
00612   unsigned long num = 2;
00613 
00614   if (otk::Property::get(_window, otk::Property::atoms.wm_class,
00615                          otk::Property::ascii, &num, &v)) {
00616     if (num > 0) _app_name = v[0].c_str();
00617     if (num > 1) _app_class = v[1].c_str();
00618   }
00619 
00620   v.clear();
00621   num = 1;
00622   if (otk::Property::get(_window, otk::Property::atoms.wm_window_role,
00623                          otk::Property::ascii, &num, &v)) {
00624     if (num > 0) _role = v[0].c_str();
00625   }
00626 }
00627 
00628 
00629 void Client::updateStrut()
00630 {
00631   unsigned long num = 4;
00632   unsigned long *data;
00633   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_strut,
00634                           otk::Property::atoms.cardinal, &num, &data))
00635     return;
00636 
00637   if (num == 4) {
00638     _strut.left = data[0];
00639     _strut.right = data[1];
00640     _strut.top = data[2];
00641     _strut.bottom = data[3]; 
00642 
00643     // updating here is pointless while we're being mapped cuz we're not in
00644     // the screen's client list yet
00645     if (frame)
00646       openbox->screen(_screen)->updateStrut();
00647   }
00648 
00649   delete [] data;
00650 }
00651 
00652 
00653 void Client::updateTransientFor()
00654 {
00655   Window t = 0;
00656   Client *c = 0;
00657 
00658   if (XGetTransientForHint(**otk::display, _window, &t) &&
00659       t != _window) { // cant be transient to itself!
00660     c = openbox->findClient(t);
00661     assert(c != this); // if this happens then we need to check for it
00662 
00663     if (!c /*XXX: && _group*/) {
00664       // not transient to a client, see if it is transient for a group
00665       if (//t == _group->leader() ||
00666         t == None ||
00667         t == otk::display->screenInfo(_screen)->rootWindow()) {
00668         // window is a transient for its group!
00669         // XXX: for now this is treated as non-transient.
00670         //      this needs to be fixed!
00671       }
00672     }
00673   }
00674 
00675   // if anything has changed...
00676   if (c != _transient_for) {
00677     bool m = _modal;
00678     if (_modal)
00679       setModal(false);
00680     
00681     if (_transient_for)
00682       _transient_for->_transients.remove(this); // remove from old parent
00683     _transient_for = c;
00684     if (_transient_for)
00685       _transient_for->_transients.push_back(this); // add to new parent
00686 
00687     if (m)
00688       setModal(true);
00689   }
00690 }
00691 
00692 
00693 void Client::propertyHandler(const XPropertyEvent &e)
00694 {
00695   otk::EventHandler::propertyHandler(e);
00696 
00697   // validate cuz we query stuff off the client here
00698   if (!validate()) return;
00699   
00700   // compress changes to a single property into a single change
00701   XEvent ce;
00702   while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
00703     // XXX: it would be nice to compress ALL changes to a property, not just
00704     //      changes in a row without other props between.
00705     if (ce.xproperty.atom != e.atom) {
00706       XPutBackEvent(**otk::display, &ce);
00707       break;
00708     }
00709   }
00710 
00711   if (e.atom == XA_WM_NORMAL_HINTS) {
00712     updateNormalHints();
00713     setupDecorAndFunctions(); // normal hints can make a window non-resizable
00714   } else if (e.atom == XA_WM_HINTS)
00715     updateWMHints();
00716   else if (e.atom == XA_WM_TRANSIENT_FOR) {
00717     updateTransientFor();
00718     getType();
00719     calcLayer(); // type may have changed, so update the layer
00720     setupDecorAndFunctions();
00721   }
00722   else if (e.atom == otk::Property::atoms.net_wm_name ||
00723            e.atom == otk::Property::atoms.wm_name)
00724     updateTitle();
00725   else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
00726            e.atom == otk::Property::atoms.wm_icon_name)
00727     updateIconTitle();
00728   else if (e.atom == otk::Property::atoms.wm_class)
00729     updateClass();
00730   else if (e.atom == otk::Property::atoms.wm_protocols) {
00731     updateProtocols();
00732     setupDecorAndFunctions();
00733   }
00734   else if (e.atom == otk::Property::atoms.net_wm_strut)
00735     updateStrut();
00736 }
00737 
00738 
00739 void Client::setWMState(long state)
00740 {
00741   if (state == _wmstate) return; // no change
00742   
00743   switch (state) {
00744   case IconicState:
00745     setDesktop(ICONIC_DESKTOP);
00746     break;
00747   case NormalState:
00748     setDesktop(openbox->screen(_screen)->desktop());
00749     break;
00750   }
00751 }
00752 
00753 
00754 void Client::setDesktop(long target)
00755 {
00756   if (target == _desktop) return;
00757   
00758   printf("Setting desktop %ld\n", target);
00759 
00760   if (!(target >= 0 || target == (signed)0xffffffff ||
00761         target == ICONIC_DESKTOP))
00762     return;
00763   
00764   _desktop = target;
00765 
00766   // set the desktop hint, but not if we're iconifying
00767   if (_desktop != ICONIC_DESKTOP)
00768     otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
00769                        otk::Property::atoms.cardinal, (unsigned)_desktop);
00770   
00771   // 'move' the window to the new desktop
00772   if (_desktop == openbox->screen(_screen)->desktop() ||
00773       _desktop == (signed)0xffffffff)
00774     frame->show();
00775   else
00776     frame->hide();
00777 
00778   // Handle Iconic state. Iconic state is maintained by the client being a
00779   // member of the ICONIC_DESKTOP, so this is where we make iconifying and
00780   // uniconifying happen.
00781   bool i = _desktop == ICONIC_DESKTOP;
00782   if (i != _iconic) { // has the state changed?
00783     _iconic = i;
00784     if (_iconic) {
00785       _wmstate = IconicState;
00786       ignore_unmaps++;
00787       // we unmap the client itself so that we can get MapRequest events, and
00788       // because the ICCCM tells us to!
00789       XUnmapWindow(**otk::display, _window);
00790     } else {
00791       _wmstate = NormalState;
00792       XMapWindow(**otk::display, _window);
00793     }
00794     changeState();
00795   }
00796   
00797   frame->adjustState();
00798 }
00799 
00800 
00801 Client *Client::findModalChild(Client *skip) const
00802 {
00803   Client *ret = 0;
00804   
00805   // find a modal child recursively and try focus it
00806   List::const_iterator it, end = _transients.end();
00807   for (it = _transients.begin(); it != end; ++it)
00808     if ((*it)->_modal && *it != skip)
00809       return *it; // got one
00810   // none of our direct children are modal, let them try check
00811   for (it = _transients.begin(); it != end; ++it)
00812     if ((ret = (*it)->findModalChild()))
00813       return ret; // got one
00814   return ret;
00815 }
00816 
00817 
00818 void Client::setModal(bool modal)
00819 {
00820   if (modal == _modal) return;
00821   
00822   if (modal) {
00823     Client *c = this;
00824     while (c->_transient_for) {
00825       c = c->_transient_for;
00826       if (c->_modal_child) break; // already has a modal child
00827       c->_modal_child = this;
00828     }
00829   } else {
00830     // try find a replacement modal dialog
00831     Client *replacement = 0;
00832     
00833     Client *c = this;
00834     while (c->_transient_for) // go up the tree
00835       c = c->_transient_for;
00836     replacement = c->findModalChild(this); // find a modal child, skipping this
00837 
00838     c = this;
00839     while (c->_transient_for) {
00840       c = c->_transient_for;
00841       if (c->_modal_child != this) break; // has a different modal child
00842       c->_modal_child = replacement;
00843     }
00844   }
00845   _modal = modal;
00846 }
00847 
00848 
00849 void Client::setState(StateAction action, long data1, long data2)
00850 {
00851   bool shadestate = _shaded;
00852   bool fsstate = _fullscreen;
00853   bool maxh = _max_horz;
00854   bool maxv = _max_vert;
00855   bool modal = _modal;
00856 
00857   if (!(action == State_Add || action == State_Remove ||
00858         action == State_Toggle))
00859     return; // an invalid action was passed to the client message, ignore it
00860 
00861   for (int i = 0; i < 2; ++i) {
00862     Atom state = i == 0 ? data1 : data2;
00863     
00864     if (! state) continue;
00865 
00866     // if toggling, then pick whether we're adding or removing
00867     if (action == State_Toggle) {
00868       if (state == otk::Property::atoms.net_wm_state_modal)
00869         action = _modal ? State_Remove : State_Add;
00870       else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
00871         action = _max_vert ? State_Remove : State_Add;
00872       else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
00873         action = _max_horz ? State_Remove : State_Add;
00874       else if (state == otk::Property::atoms.net_wm_state_shaded)
00875         action = _shaded ? State_Remove : State_Add;
00876       else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
00877         action = _skip_taskbar ? State_Remove : State_Add;
00878       else if (state == otk::Property::atoms.net_wm_state_skip_pager)
00879         action = _skip_pager ? State_Remove : State_Add;
00880       else if (state == otk::Property::atoms.net_wm_state_fullscreen)
00881         action = _fullscreen ? State_Remove : State_Add;
00882       else if (state == otk::Property::atoms.net_wm_state_above)
00883         action = _above ? State_Remove : State_Add;
00884       else if (state == otk::Property::atoms.net_wm_state_below)
00885         action = _below ? State_Remove : State_Add;
00886     }
00887     
00888     if (action == State_Add) {
00889       if (state == otk::Property::atoms.net_wm_state_modal) {
00890         if (_modal) continue;
00891         modal = true;
00892       } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
00893         maxv = true;
00894       } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
00895         if (_max_horz) continue;
00896         maxh = true;
00897       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
00898         shadestate = true;
00899       } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
00900         _skip_taskbar = true;
00901       } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
00902         _skip_pager = true;
00903       } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
00904         fsstate = true;
00905       } else if (state == otk::Property::atoms.net_wm_state_above) {
00906         if (_above) continue;
00907         _above = true;
00908       } else if (state == otk::Property::atoms.net_wm_state_below) {
00909         if (_below) continue;
00910         _below = true;
00911       }
00912 
00913     } else { // action == State_Remove
00914       if (state == otk::Property::atoms.net_wm_state_modal) {
00915         if (!_modal) continue;
00916         modal = false;
00917       } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
00918         maxv = false;
00919       } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
00920         maxh = false;
00921       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
00922         shadestate = false;
00923       } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
00924         _skip_taskbar = false;
00925       } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
00926         _skip_pager = false;
00927       } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
00928         fsstate = false;
00929       } else if (state == otk::Property::atoms.net_wm_state_above) {
00930         if (!_above) continue;
00931         _above = false;
00932       } else if (state == otk::Property::atoms.net_wm_state_below) {
00933         if (!_below) continue;
00934         _below = false;
00935       }
00936     }
00937   }
00938   if (maxh != _max_horz || maxv != _max_vert) {
00939     if (maxh != _max_horz && maxv != _max_vert) { // toggling both
00940       if (maxh == maxv) { // both going the same way
00941         maximize(maxh, 0, true);
00942       } else {
00943         maximize(maxh, 1, true);
00944         maximize(maxv, 2, true);
00945       }
00946     } else { // toggling one
00947       if (maxh != _max_horz)
00948         maximize(maxh, 1, true);
00949       else
00950         maximize(maxv, 2, true);
00951     }
00952   }
00953   if (modal != _modal)
00954     setModal(modal);
00955   // change fullscreen state before shading, as it will affect if the window
00956   // can shade or not
00957   if (fsstate != _fullscreen)
00958     fullscreen(fsstate, true);
00959   if (shadestate != _shaded)
00960     shade(shadestate);
00961   calcLayer();
00962   changeState(); // change the hint to relect these changes
00963 }
00964 
00965 
00966 void Client::toggleClientBorder(bool addborder)
00967 {
00968   // adjust our idea of where the client is, based on its border. When the
00969   // border is removed, the client should now be considered to be in a
00970   // different position.
00971   // when re-adding the border to the client, the same operation needs to be
00972   // reversed.
00973   int oldx = _area.x(), oldy = _area.y();
00974   int x = oldx, y = oldy;
00975   switch(_gravity) {
00976   default:
00977   case NorthWestGravity:
00978   case WestGravity:
00979   case SouthWestGravity:
00980     break;
00981   case NorthEastGravity:
00982   case EastGravity:
00983   case SouthEastGravity:
00984     if (addborder) x -= _border_width * 2;
00985     else           x += _border_width * 2;
00986     break;
00987   case NorthGravity:
00988   case SouthGravity:
00989   case CenterGravity:
00990   case ForgetGravity:
00991   case StaticGravity:
00992     if (addborder) x -= _border_width;
00993     else           x += _border_width;
00994     break;
00995   }
00996   switch(_gravity) {
00997   default:
00998   case NorthWestGravity:
00999   case NorthGravity:
01000   case NorthEastGravity:
01001     break;
01002   case SouthWestGravity:
01003   case SouthGravity:
01004   case SouthEastGravity:
01005     if (addborder) y -= _border_width * 2;
01006     else           y += _border_width * 2;
01007     break;
01008   case WestGravity:
01009   case EastGravity:
01010   case CenterGravity:
01011   case ForgetGravity:
01012   case StaticGravity:
01013     if (addborder) y -= _border_width;
01014     else           y += _border_width;
01015     break;
01016   }
01017   _area.setPos(x, y);
01018 
01019   if (addborder) {
01020     XSetWindowBorderWidth(**otk::display, _window, _border_width);
01021 
01022     // move the client so it is back it the right spot _with_ its border!
01023     if (x != oldx || y != oldy)
01024       XMoveWindow(**otk::display, _window, x, y);
01025   } else
01026     XSetWindowBorderWidth(**otk::display, _window, 0);
01027 }
01028 
01029 
01030 void Client::clientMessageHandler(const XClientMessageEvent &e)
01031 {
01032   otk::EventHandler::clientMessageHandler(e);
01033   
01034   // validate cuz we query stuff off the client here
01035   if (!validate()) return;
01036   
01037   if (e.format != 32) return;
01038 
01039   if (e.message_type == otk::Property::atoms.wm_change_state) {
01040     // compress changes into a single change
01041     bool compress = false;
01042     XEvent ce;
01043     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
01044       // XXX: it would be nice to compress ALL messages of a type, not just
01045       //      messages in a row without other message types between.
01046       if (ce.xclient.message_type != e.message_type) {
01047         XPutBackEvent(**otk::display, &ce);
01048         break;
01049       }
01050       compress = true;
01051     }
01052     if (compress)
01053       setWMState(ce.xclient.data.l[0]); // use the found event
01054     else
01055       setWMState(e.data.l[0]); // use the original event
01056   } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
01057     // compress changes into a single change 
01058     bool compress = false;
01059     XEvent ce;
01060     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
01061       // XXX: it would be nice to compress ALL messages of a type, not just
01062       //      messages in a row without other message types between.
01063       if (ce.xclient.message_type != e.message_type) {
01064         XPutBackEvent(**otk::display, &ce);
01065         break;
01066       }
01067       compress = true;
01068     }
01069     if (compress)
01070       setDesktop(e.data.l[0]); // use the found event
01071     else
01072       setDesktop(e.data.l[0]); // use the original event
01073   } else if (e.message_type == otk::Property::atoms.net_wm_state) {
01074     // can't compress these
01075 #ifdef DEBUG
01076     printf("net_wm_state %s %ld %ld for 0x%lx\n",
01077            (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
01078             e.data.l[0] == 2 ? "Toggle" : "INVALID"),
01079            e.data.l[1], e.data.l[2], _window);
01080 #endif
01081     setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
01082   } else if (e.message_type == otk::Property::atoms.net_close_window) {
01083 #ifdef DEBUG
01084     printf("net_close_window for 0x%lx\n", _window);
01085 #endif
01086     close();
01087   } else if (e.message_type == otk::Property::atoms.net_active_window) {
01088 #ifdef DEBUG
01089     printf("net_active_window for 0x%lx\n", _window);
01090 #endif
01091     if (_iconic)
01092       setDesktop(openbox->screen(_screen)->desktop());
01093     if (_shaded)
01094       shade(false);
01095     focus();
01096     openbox->screen(_screen)->raiseWindow(this);
01097   } else if (e.message_type == otk::Property::atoms.openbox_active_window) {
01098     if (_iconic)
01099       setDesktop(openbox->screen(_screen)->desktop());
01100     if (e.data.l[0] && _shaded)
01101       shade(false);
01102     focus();
01103     if (e.data.l[1])
01104       openbox->screen(_screen)->raiseWindow(this);
01105   }
01106 }
01107 
01108 
01109 #if defined(SHAPE)
01110 void Client::shapeHandler(const XShapeEvent &e)
01111 {
01112   otk::EventHandler::shapeHandler(e);
01113 
01114   if (e.kind == ShapeBounding) {
01115     _shaped = e.shaped;
01116     frame->adjustShape();
01117   }
01118 }
01119 #endif
01120 
01121 
01122 void Client::resize(Corner anchor, int w, int h)
01123 {
01124   if (!(_functions & Func_Resize)) return;
01125   internal_resize(anchor, w, h);
01126 }
01127 
01128 
01129 void Client::internal_resize(Corner anchor, int w, int h, bool user,
01130                              int x, int y)
01131 {
01132   w -= _base_size.x(); 
01133   h -= _base_size.y();
01134 
01135   if (user) {
01136     // for interactive resizing. have to move half an increment in each
01137     // direction.
01138     int mw = w % _size_inc.x(); // how far we are towards the next size inc
01139     int mh = h % _size_inc.y();
01140     int aw = _size_inc.x() / 2; // amount to add
01141     int ah = _size_inc.y() / 2;
01142     // don't let us move into a new size increment
01143     if (mw + aw >= _size_inc.x()) aw = _size_inc.x() - mw - 1;
01144     if (mh + ah >= _size_inc.y()) ah = _size_inc.y() - mh - 1;
01145     w += aw;
01146     h += ah;
01147     
01148     // if this is a user-requested resize, then check against min/max sizes
01149     // and aspect ratios
01150 
01151     // smaller than min size or bigger than max size?
01152     if (w < _min_size.x()) w = _min_size.x();
01153     else if (w > _max_size.x()) w = _max_size.x();
01154     if (h < _min_size.y()) h = _min_size.y();
01155     else if (h > _max_size.y()) h = _max_size.y();
01156 
01157     // adjust the height ot match the width for the aspect ratios
01158     if (_min_ratio)
01159       if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
01160     if (_max_ratio)
01161       if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
01162   }
01163 
01164   // keep to the increments
01165   w /= _size_inc.x();
01166   h /= _size_inc.y();
01167 
01168   // you cannot resize to nothing
01169   if (w < 1) w = 1;
01170   if (h < 1) h = 1;
01171   
01172   // store the logical size
01173   _logical_size.setPoint(w, h);
01174 
01175   w *= _size_inc.x();
01176   h *= _size_inc.y();
01177 
01178   w += _base_size.x();
01179   h += _base_size.y();
01180 
01181   if (x == INT_MIN || y == INT_MIN) {
01182     x = _area.x();
01183     y = _area.y();
01184     switch (anchor) {
01185     case TopLeft:
01186       break;
01187     case TopRight:
01188       x -= w - _area.width();
01189       break;
01190     case BottomLeft:
01191       y -= h - _area.height();
01192       break;
01193     case BottomRight:
01194       x -= w - _area.width();
01195       y -= h - _area.height();
01196       break;
01197     }
01198   }
01199 
01200   _area.setSize(w, h);
01201 
01202   XResizeWindow(**otk::display, _window, w, h);
01203 
01204   // resize the frame to match the request
01205   frame->adjustSize();
01206   internal_move(x, y);
01207 }
01208 
01209 
01210 void Client::move(int x, int y)
01211 {
01212   if (!(_functions & Func_Move)) return;
01213   frame->frameGravity(x, y); // get the client's position based on x,y for the
01214                              // frame
01215   internal_move(x, y);
01216 }
01217 
01218 
01219 void Client::internal_move(int x, int y)
01220 {
01221   _area.setPos(x, y);
01222 
01223   // move the frame to be in the requested position
01224   if (frame) { // this can be called while mapping, before frame exists
01225     frame->adjustPosition();
01226 
01227     // send synthetic configure notify (we don't need to if we aren't mapped
01228     // yet)
01229     XEvent event;
01230     event.type = ConfigureNotify;
01231     event.xconfigure.display = **otk::display;
01232     event.xconfigure.event = _window;
01233     event.xconfigure.window = _window;
01234     
01235     // root window coords with border in mind
01236     event.xconfigure.x = x - _border_width + frame->size().left;
01237     event.xconfigure.y = y - _border_width + frame->size().top;
01238     
01239     event.xconfigure.width = _area.width();
01240     event.xconfigure.height = _area.height();
01241     event.xconfigure.border_width = _border_width;
01242     event.xconfigure.above = frame->plate();
01243     event.xconfigure.override_redirect = False;
01244     XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
01245                StructureNotifyMask, &event);
01246 #if 0//def DEBUG
01247     printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
01248            event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
01249            event.xconfigure.height, event.xconfigure.window);
01250 #endif
01251   }
01252 }
01253 
01254 
01255 void Client::close()
01256 {
01257   XEvent ce;
01258 
01259   if (!(_functions & Func_Close)) return;
01260 
01261   // XXX: itd be cool to do timeouts and shit here for killing the client's
01262   //      process off
01263   // like... if the window is around after 5 seconds, then the close button
01264   // turns a nice red, and if this function is called again, the client is
01265   // explicitly killed.
01266 
01267   ce.xclient.type = ClientMessage;
01268   ce.xclient.message_type =  otk::Property::atoms.wm_protocols;
01269   ce.xclient.display = **otk::display;
01270   ce.xclient.window = _window;
01271   ce.xclient.format = 32;
01272   ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
01273   ce.xclient.data.l[1] = CurrentTime;
01274   ce.xclient.data.l[2] = 0l;
01275   ce.xclient.data.l[3] = 0l;
01276   ce.xclient.data.l[4] = 0l;
01277   XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
01278 }
01279 
01280 
01281 void Client::changeState()
01282 {
01283   unsigned long state[2];
01284   state[0] = _wmstate;
01285   state[1] = None;
01286   otk::Property::set(_window, otk::Property::atoms.wm_state,
01287                      otk::Property::atoms.wm_state, state, 2);
01288 
01289   Atom netstate[10];
01290   int num = 0;
01291   if (_modal)
01292     netstate[num++] = otk::Property::atoms.net_wm_state_modal;
01293   if (_shaded)
01294     netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
01295   if (_iconic)
01296     netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
01297   if (_skip_taskbar)
01298     netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
01299   if (_skip_pager)
01300     netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
01301   if (_fullscreen)
01302     netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
01303   if (_max_vert)
01304     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
01305   if (_max_horz)
01306     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
01307   if (_above)
01308     netstate[num++] = otk::Property::atoms.net_wm_state_above;
01309   if (_below)
01310     netstate[num++] = otk::Property::atoms.net_wm_state_below;
01311   otk::Property::set(_window, otk::Property::atoms.net_wm_state,
01312                      otk::Property::atoms.atom, netstate, num);
01313 
01314   calcLayer();
01315 
01316   if (frame)
01317     frame->adjustState();
01318 }
01319 
01320 
01321 void Client::changeAllowedActions(void)
01322 {
01323   Atom actions[9];
01324   int num = 0;
01325 
01326   actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
01327 
01328   if (_functions & Func_Shade)
01329     actions[num++] = otk::Property::atoms.net_wm_action_shade;
01330   if (_functions & Func_Close)
01331     actions[num++] = otk::Property::atoms.net_wm_action_close;
01332   if (_functions & Func_Move)
01333     actions[num++] = otk::Property::atoms.net_wm_action_move;
01334   if (_functions & Func_Iconify)
01335     actions[num++] = otk::Property::atoms.net_wm_action_minimize;
01336   if (_functions & Func_Resize)
01337     actions[num++] = otk::Property::atoms.net_wm_action_resize;
01338   if (_functions & Func_Fullscreen)
01339     actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
01340   if (_functions & Func_Maximize) {
01341     actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
01342     actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
01343   }
01344 
01345   otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
01346                      otk::Property::atoms.atom, actions, num);
01347 }
01348 
01349 
01350 void Client::remaximize()
01351 {
01352   int dir;
01353   if (_max_horz && _max_vert)
01354     dir = 0;
01355   else if (_max_horz)
01356     dir = 1;
01357   else if (_max_vert)
01358     dir = 2;
01359   else
01360     return; // not maximized
01361   _max_horz = _max_vert = false;
01362   maximize(true, dir, false);
01363 }
01364 
01365 
01366 void Client::applyStartupState()
01367 {
01368   // these are in a carefully crafted order..
01369 
01370   if (_modal) {
01371     _modal = false;
01372     setModal(true);
01373   }
01374   
01375   if (_iconic) {
01376     _iconic = false;
01377     setDesktop(ICONIC_DESKTOP);
01378   }
01379   if (_fullscreen) {
01380     _fullscreen = false;
01381     fullscreen(true, false);
01382   }
01383   if (_shaded) {
01384     _shaded = false;
01385     shade(true);
01386   }
01387   if (_urgent)
01388     fireUrgent();
01389   
01390   if (_max_vert && _max_horz) {
01391     _max_vert = _max_horz = false;
01392     maximize(true, 0, false);
01393   } else if (_max_vert) {
01394     _max_vert = false;
01395     maximize(true, 2, false);
01396   } else if (_max_horz) {
01397     _max_horz = false;
01398     maximize(true, 1, false);
01399   }
01400 
01401   if (_skip_taskbar); // nothing to do for this
01402   if (_skip_pager);   // nothing to do for this
01403   if (_modal);        // nothing to do for this
01404   if (_above);        // nothing to do for this
01405   if (_below);        // nothing to do for this
01406 }
01407 
01408 
01409 void Client::fireUrgent()
01410 {
01411   // call the python UrgentWindow callbacks
01412   EventData data(_screen, this, EventAction::UrgentWindow, 0);
01413   openbox->bindings()->fireEvent(&data);
01414 }
01415 
01416 
01417 void Client::shade(bool shade)
01418 {
01419   if (!(_functions & Func_Shade) || // can't
01420       _shaded == shade) return;     // already done
01421 
01422   // when we're iconic, don't change the wmstate
01423   if (!_iconic)
01424     _wmstate = shade ? IconicState : NormalState;
01425   _shaded = shade;
01426   changeState();
01427   frame->adjustSize();
01428 }
01429 
01430 
01431 void Client::maximize(bool max, int dir, bool savearea)
01432 {
01433   assert(dir == 0 || dir == 1 || dir == 2);
01434   if (!(_functions & Func_Maximize)) return; // can't
01435 
01436   // check if already done
01437   if (max) {
01438     if (dir == 0 && _max_horz && _max_vert) return;
01439     if (dir == 1 && _max_horz) return;
01440     if (dir == 2 && _max_vert) return;
01441   } else {
01442     if (dir == 0 && !_max_horz && !_max_vert) return;
01443     if (dir == 1 && !_max_horz) return;
01444     if (dir == 2 && !_max_vert) return;
01445   }
01446 
01447   const otk::Rect &a = openbox->screen(_screen)->area();
01448   int x = frame->rect().x(), y = frame->rect().y(),
01449     w = _area.width(), h = _area.height();
01450   
01451   if (max) {
01452     if (savearea) {
01453       long dimensions[4];
01454       long *readdim;
01455       unsigned long n = 4;
01456 
01457       dimensions[0] = x;
01458       dimensions[1] = y;
01459       dimensions[2] = w;
01460       dimensions[3] = h;
01461 
01462       // get the property off the window and use it for the dimentions we are
01463       // already maxed on
01464       if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
01465                              otk::Property::atoms.cardinal, &n,
01466                              (long unsigned**) &readdim)) {
01467         if (n >= 4) {
01468           if (_max_horz) {
01469             dimensions[0] = readdim[0];
01470             dimensions[2] = readdim[2];
01471           }
01472           if (_max_vert) {
01473             dimensions[1] = readdim[1];
01474             dimensions[3] = readdim[3];
01475           }
01476         }
01477         delete readdim;
01478       }
01479       
01480       otk::Property::set(_window, otk::Property::atoms.openbox_premax,
01481                          otk::Property::atoms.cardinal,
01482                          (long unsigned*)dimensions, 4);
01483     }
01484     if (dir == 0 || dir == 1) { // horz
01485       x = a.x();
01486       w = a.width();
01487     }
01488     if (dir == 0 || dir == 2) { // vert
01489       y = a.y();
01490       h = a.height() - frame->size().top - frame->size().bottom;
01491     }
01492   } else {
01493     long *dimensions;
01494     long unsigned n = 4;
01495       
01496     if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
01497                            otk::Property::atoms.cardinal, &n,
01498                            (long unsigned**) &dimensions)) {
01499       if (n >= 4) {
01500         if (dir == 0 || dir == 1) { // horz
01501           x = (signed int)dimensions[0];
01502           w = (signed int)dimensions[2];
01503         }
01504         if (dir == 0 || dir == 2) { // vert
01505           y = (signed int)dimensions[1];
01506           h = (signed int)dimensions[3];
01507         }
01508       }
01509       delete dimensions;
01510     } else {
01511       // pick some fallbacks...
01512       if (dir == 0 || dir == 1) { // horz
01513         x = a.x() + a.width() / 4;
01514         w = a.width() / 2;
01515       }
01516       if (dir == 0 || dir == 2) { // vert
01517         y = a.y() + a.height() / 4;
01518         h = a.height() / 2;
01519       }
01520     }
01521   }
01522 
01523   if (dir == 0 || dir == 1) // horz
01524     _max_horz = max;
01525   if (dir == 0 || dir == 2) // vert
01526     _max_vert = max;
01527 
01528   if (!_max_horz && !_max_vert)
01529     otk::Property::erase(_window, otk::Property::atoms.openbox_premax);
01530 
01531   changeState(); // change the state hints on the client
01532 
01533   frame->frameGravity(x, y); // figure out where the client should be going
01534   internal_resize(TopLeft, w, h, true, x, y);
01535 }
01536 
01537 
01538 void Client::fullscreen(bool fs, bool savearea)
01539 {
01540   static FunctionFlags saved_func;
01541   static DecorationFlags saved_decor;
01542 
01543   if (!(_functions & Func_Fullscreen) || // can't
01544       _fullscreen == fs) return;         // already done
01545 
01546   _fullscreen = fs;
01547   changeState(); // change the state hints on the client
01548 
01549   int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height();
01550   
01551   if (fs) {
01552     // save the functions and remove them
01553     saved_func = _functions;
01554     _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
01555     // save the decorations and remove them
01556     saved_decor = _decorations;
01557     _decorations = 0;
01558     if (savearea) {
01559       long dimensions[4];
01560       dimensions[0] = _area.x();
01561       dimensions[1] = _area.y();
01562       dimensions[2] = _area.width();
01563       dimensions[3] = _area.height();
01564       otk::Property::set(_window, otk::Property::atoms.openbox_premax,
01565                          otk::Property::atoms.cardinal,
01566                          (long unsigned*)dimensions, 4);
01567     }
01568     const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
01569     x = 0;
01570     y = 0;
01571     w = info->width();
01572     h = info->height();
01573   } else {
01574     _functions = saved_func;
01575     _decorations = saved_decor;
01576 
01577     long *dimensions;
01578     long unsigned n = 4;
01579       
01580     if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
01581                            otk::Property::atoms.cardinal, &n,
01582                            (long unsigned**) &dimensions)) {
01583       if (n >= 4) {
01584         x = dimensions[0];
01585         y = dimensions[1];
01586         w = dimensions[2];
01587         h = dimensions[3];
01588       }
01589       delete dimensions;
01590     } else {
01591       // pick some fallbacks...
01592       const otk::Rect &a = openbox->screen(_screen)->area();
01593       x = a.x() + a.width() / 4;
01594       y = a.y() + a.height() / 4;
01595       w = a.width() / 2;
01596         h = a.height() / 2;
01597     }    
01598   }
01599   
01600   changeAllowedActions();  // based on the new _functions
01601 
01602   // when fullscreening, don't obey things like increments, fill the screen
01603   internal_resize(TopLeft, w, h, !fs, x, y);
01604 
01605   // raise (back) into our stacking layer
01606   openbox->screen(_screen)->raiseWindow(this);
01607 
01608   // try focus us when we go into fullscreen mode
01609   if (fs) focus();
01610 }
01611 
01612 
01613 void Client::disableDecorations(DecorationFlags flags)
01614 {
01615   _disabled_decorations = flags;
01616   setupDecorAndFunctions();
01617 }
01618 
01619 
01620 void Client::installColormap(bool install) const
01621 {
01622   XWindowAttributes wa;
01623   if (XGetWindowAttributes(**otk::display, _window, &wa)) {
01624     if (install)
01625       XInstallColormap(**otk::display, wa.colormap);
01626     else
01627       XUninstallColormap(**otk::display, wa.colormap);
01628   }
01629 }
01630 
01631 
01632 bool Client::focus()
01633 {
01634   // if we have a modal child, then focus it, not us
01635   if (_modal_child)
01636     return _modal_child->focus();
01637 
01638   // won't try focus if the client doesn't want it, or if the window isn't
01639   // visible on the screen
01640   if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
01641 
01642   if (_focused) return true;
01643 
01644   // do a check to see if the window has already been unmapped or destroyed
01645   // do this intelligently while watching out for unmaps we've generated
01646   // (ignore_unmaps > 0)
01647   XEvent ev;
01648   if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
01649     XPutBackEvent(**otk::display, &ev);
01650     return false;
01651   }
01652   while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
01653     if (ignore_unmaps) {
01654       unmapHandler(ev.xunmap);
01655     } else {
01656       XPutBackEvent(**otk::display, &ev);
01657       return false;
01658     }
01659   }
01660 
01661   if (_can_focus)
01662     XSetInputFocus(**otk::display, _window,
01663                    RevertToNone, CurrentTime);
01664 
01665   if (_focus_notify) {
01666     XEvent ce;
01667     ce.xclient.type = ClientMessage;
01668     ce.xclient.message_type = otk::Property::atoms.wm_protocols;
01669     ce.xclient.display = **otk::display;
01670     ce.xclient.window = _window;
01671     ce.xclient.format = 32;
01672     ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
01673     ce.xclient.data.l[1] = openbox->lastTime();
01674     ce.xclient.data.l[2] = 0l;
01675     ce.xclient.data.l[3] = 0l;
01676     ce.xclient.data.l[4] = 0l;
01677     XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
01678   }
01679 
01680   XSync(**otk::display, False);
01681   return true;
01682 }
01683 
01684 
01685 void Client::unfocus() const
01686 {
01687   if (!_focused) return;
01688 
01689   assert(openbox->focusedClient() == this);
01690   openbox->setFocusedClient(0);
01691 }
01692 
01693 
01694 void Client::focusHandler(const XFocusChangeEvent &e)
01695 {
01696 #ifdef    DEBUG
01697 //  printf("FocusIn for 0x%lx\n", e.window);
01698 #endif // DEBUG
01699   
01700   otk::EventHandler::focusHandler(e);
01701 
01702   frame->focus();
01703   _focused = true;
01704 
01705   openbox->setFocusedClient(this);
01706 }
01707 
01708 
01709 void Client::unfocusHandler(const XFocusChangeEvent &e)
01710 {
01711 #ifdef    DEBUG
01712 //  printf("FocusOut for 0x%lx\n", e.window);
01713 #endif // DEBUG
01714   
01715   otk::EventHandler::unfocusHandler(e);
01716 
01717   frame->unfocus();
01718   _focused = false;
01719 
01720   if (openbox->focusedClient() == this)
01721     openbox->setFocusedClient(0);
01722 }
01723 
01724 
01725 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
01726 {
01727 #ifdef    DEBUG
01728   printf("ConfigureRequest for 0x%lx\n", e.window);
01729 #endif // DEBUG
01730   
01731   otk::EventHandler::configureRequestHandler(e);
01732 
01733   // if we are iconic (or shaded (fvwm does this)) ignore the event
01734   if (_iconic || _shaded) return;
01735 
01736   if (e.value_mask & CWBorderWidth)
01737     _border_width = e.border_width;
01738 
01739   // resize, then move, as specified in the EWMH section 7.7
01740   if (e.value_mask & (CWWidth | CWHeight)) {
01741     int w = (e.value_mask & CWWidth) ? e.width : _area.width();
01742     int h = (e.value_mask & CWHeight) ? e.height : _area.height();
01743 
01744     Corner corner;
01745     switch (_gravity) {
01746     case NorthEastGravity:
01747     case EastGravity:
01748       corner = TopRight;
01749       break;
01750     case SouthWestGravity:
01751     case SouthGravity:
01752       corner = BottomLeft;
01753       break;
01754     case SouthEastGravity:
01755       corner = BottomRight;
01756       break;
01757     default:     // NorthWest, Static, etc
01758       corner = TopLeft;
01759     }
01760 
01761     // if moving AND resizing ...
01762     if (e.value_mask & (CWX | CWY)) {
01763       int x = (e.value_mask & CWX) ? e.x : _area.x();
01764       int y = (e.value_mask & CWY) ? e.y : _area.y();
01765       internal_resize(corner, w, h, false, x, y);
01766     } else // if JUST resizing...
01767       internal_resize(corner, w, h, false);
01768   } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
01769     int x = (e.value_mask & CWX) ? e.x : _area.x();
01770     int y = (e.value_mask & CWY) ? e.y : _area.y();
01771     internal_move(x, y);
01772   }
01773 
01774   if (e.value_mask & CWStackMode) {
01775     switch (e.detail) {
01776     case Below:
01777     case BottomIf:
01778       openbox->screen(_screen)->lowerWindow(this);
01779       break;
01780 
01781     case Above:
01782     case TopIf:
01783     default:
01784       openbox->screen(_screen)->raiseWindow(this);
01785       break;
01786     }
01787   }
01788 }
01789 
01790 
01791 void Client::unmapHandler(const XUnmapEvent &e)
01792 {
01793   if (ignore_unmaps) {
01794 #ifdef    DEBUG
01795 //  printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
01796 #endif // DEBUG
01797     ignore_unmaps--;
01798     return;
01799   }
01800   
01801 #ifdef    DEBUG
01802   printf("UnmapNotify for 0x%lx\n", e.window);
01803 #endif // DEBUG
01804 
01805   otk::EventHandler::unmapHandler(e);
01806 
01807   // this deletes us etc
01808   openbox->screen(_screen)->unmanageWindow(this);
01809 }
01810 
01811 
01812 void Client::destroyHandler(const XDestroyWindowEvent &e)
01813 {
01814 #ifdef    DEBUG
01815   printf("DestroyNotify for 0x%lx\n", e.window);
01816 #endif // DEBUG
01817 
01818   otk::EventHandler::destroyHandler(e);
01819 
01820   // this deletes us etc
01821   openbox->screen(_screen)->unmanageWindow(this);
01822 }
01823 
01824 
01825 void Client::reparentHandler(const XReparentEvent &e)
01826 {
01827   // this is when the client is first taken captive in the frame
01828   if (e.parent == frame->plate()) return;
01829 
01830 #ifdef    DEBUG
01831   printf("ReparentNotify for 0x%lx\n", e.window);
01832 #endif // DEBUG
01833 
01834   otk::EventHandler::reparentHandler(e);
01835 
01836   /*
01837     This event is quite rare and is usually handled in unmapHandler.
01838     However, if the window is unmapped when the reparent event occurs,
01839     the window manager never sees it because an unmap event is not sent
01840     to an already unmapped window.
01841   */
01842 
01843   // we don't want the reparent event, put it back on the stack for the X
01844   // server to deal with after we unmanage the window
01845   XEvent ev;
01846   ev.xreparent = e;
01847   XPutBackEvent(**otk::display, &ev);
01848   
01849   // this deletes us etc
01850   openbox->screen(_screen)->unmanageWindow(this);
01851 }
01852 
01853 void Client::mapRequestHandler(const XMapRequestEvent &e)
01854 {
01855 #ifdef    DEBUG
01856   printf("MapRequest for already managed 0x%lx\n", e.window);
01857 #endif // DEBUG
01858 
01859   assert(_iconic); // we shouldn't be able to get this unless we're iconic
01860 
01861   // move to the current desktop (uniconify)
01862   setDesktop(openbox->screen(_screen)->desktop());
01863   // XXX: should we focus/raise the window? (basically a net_wm_active_window)
01864 }
01865 
01866 }

Generated on Tue Feb 4 22:58:57 2003 for Openbox by doxygen1.3-rc2