From 6f8b36ff823e0eeabc439d15255353516745e23f Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 4 Nov 2002 09:38:21 +0000 Subject: moving window.cc/hh to bbwindow.cc/hh since we'll have our own window class coming in to replace it slowly --- src/bbwindow.cc | 4220 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bbwindow.hh | 413 ++++++ src/window.cc | 4220 ------------------------------------------------------- src/window.hh | 413 ------ 4 files changed, 4633 insertions(+), 4633 deletions(-) create mode 100644 src/bbwindow.cc create mode 100644 src/bbwindow.hh delete mode 100644 src/window.cc delete mode 100644 src/window.hh (limited to 'src') diff --git a/src/bbwindow.cc b/src/bbwindow.cc new file mode 100644 index 00000000..17c53966 --- /dev/null +++ b/src/bbwindow.cc @@ -0,0 +1,4220 @@ +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- + +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif // HAVE_CONFIG_H + +extern "C" { +#include +#include + +#ifdef HAVE_STRING_H +# include +#endif // HAVE_STRING_H + +#ifdef DEBUG +# ifdef HAVE_STDIO_H +# include +# endif // HAVE_STDIO_H +#endif // DEBUG + +#ifdef HAVE_STDLIB_H +# include +#endif // HAVE_STDLIB_H +} + +#include "blackbox.hh" +#include "font.hh" +#include "gccache.hh" +#include "image.hh" +#include "screen.hh" +#include "util.hh" +#include "window.hh" +#include "workspace.hh" + +using std::string; +using std::abs; + +namespace ob { + +/* + * Initializes the class with default values/the window's set initial values. + */ +BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { + // fprintf(stderr, "BlackboxWindow size: %d bytes\n", + // sizeof(BlackboxWindow)); + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w); +#endif // DEBUG + + /* + set timer to zero... it is initialized properly later, so we check + if timer is zero in the destructor, and assume that the window is not + fully constructed if timer is zero... + */ + timer = 0; + blackbox = b; + client.window = w; + screen = s; + xatom = blackbox->getXAtom(); + + if (! validateClient()) { + delete this; + return; + } + + // fetch client size and placement + XWindowAttributes wattrib; + if (! XGetWindowAttributes(otk::OBDisplay::display, + client.window, &wattrib) || + ! wattrib.screen || wattrib.override_redirect) { +#ifdef DEBUG + fprintf(stderr, + "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n"); +#endif // DEBUG + + delete this; + return; + } + + // set the eventmask early in the game so that we make sure we get + // all the events we are interested in + XSetWindowAttributes attrib_set; + attrib_set.event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask; + XChangeWindowAttributes(otk::OBDisplay::display, client.window, + CWEventMask|CWDontPropagate, &attrib_set); + + flags.moving = flags.resizing = flags.shaded = flags.visible = + flags.iconic = flags.focused = flags.stuck = flags.modal = + flags.send_focus_message = flags.shaped = flags.skip_taskbar = + flags.skip_pager = flags.fullscreen = False; + flags.maximized = 0; + + blackbox_attrib.workspace = window_number = BSENTINEL; + + blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = 0l; + blackbox_attrib.decoration = DecorNormal; + blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0; + blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0; + + frame.border_w = 1; + frame.window = frame.plate = frame.title = frame.handle = None; + frame.close_button = frame.iconify_button = frame.maximize_button = + frame.stick_button = None; + frame.right_grip = frame.left_grip = None; + + frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel = + frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel = + frame.ubutton_pixel = frame.fbutton_pixel = frame.uborder_pixel = + frame.fborder_pixel = frame.ugrip_pixel = frame.fgrip_pixel = 0; + frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None; + frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None; + frame.ugrip = frame.fgrip = None; + + functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize; + mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border | + Decor_Iconify | Decor_Maximize; + + client.normal_hint_flags = 0; + client.window_group = None; + client.transient_for = 0; + + current_state = NormalState; + + /* + set the initial size and location of client window (relative to the + _root window_). This position is the reference point used with the + window's gravity to find the window's initial position. + */ + client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height); + client.old_bw = wattrib.border_width; + + lastButtonPressTime = 0; + + timer = new otk::OBTimer(Openbox::instance->timerManager(), + (otk::OBTimeoutHandler)timeout, + this); + timer->setTimeout(blackbox->getAutoRaiseDelay()); + + // get size, aspect, minimum/maximum size and other hints set by the + // client + + if (! getBlackboxHints()) + getNetWMHints(); + + getWMProtocols(); + getWMHints(); + getWMNormalHints(); + + frame.window = createToplevelWindow(); + + blackbox->saveWindowSearch(frame.window, this); + + frame.plate = createChildWindow(frame.window, ExposureMask); + blackbox->saveWindowSearch(frame.plate, this); + + // determine if this is a transient window + getTransientInfo(); + + // determine the window's type, so we can decide its decorations and + // functionality, or if we should not manage it at all + if (getWindowType()) { + // adjust the window decorations/behavior based on the window type + switch (window_type) { + case Type_Desktop: + case Type_Dock: + case Type_Menu: + blackbox_attrib.workspace = 0; // we do need to belong to a workspace + flags.stuck = True; // we show up on all workspaces + case Type_Splash: + // none of these windows are manipulated by the window manager + functions = 0; + break; + + case Type_Toolbar: + case Type_Utility: + // these windows get less functionality + functions &= ~(Func_Maximize | Func_Resize | Func_Iconify); + break; + + case Type_Dialog: + // dialogs cannot be maximized + functions &= ~Func_Maximize; + break; + + case Type_Normal: + // normal windows retain all of the possible decorations and + // functionality + break; + } + } else { + getMWMHints(); + } + + // further adjeust the window's decorations/behavior based on window sizes + if ((client.normal_hint_flags & PMinSize) && + (client.normal_hint_flags & PMaxSize) && + client.max_width <= client.min_width && + client.max_height <= client.min_height) { + functions &= ~(Func_Resize | Func_Maximize); + } + + setAllowedActions(); + + setupDecor(); + + if (decorations & Decor_Titlebar) + createTitlebar(); + + if (decorations & Decor_Handle) + createHandle(); + + // apply the size and gravity hint to the frame + + upsize(); + + bool place_window = True; + if (blackbox->state() == Openbox::State_Starting || isTransient() || + client.normal_hint_flags & (PPosition|USPosition)) { + applyGravity(frame.rect); + + if (blackbox->state() == Openbox::State_Starting || + client.rect.intersects(screen->getRect())) + place_window = False; + } + + // add the window's strut. note this is done *after* placing the window. + screen->addStrut(&client.strut); + updateStrut(); + + /* + the server needs to be grabbed here to prevent client's from sending + events while we are in the process of configuring their window. + We hold the grab until after we are done moving the window around. + */ + + XGrabServer(otk::OBDisplay::display); + + associateClientWindow(); + + blackbox->saveWindowSearch(client.window, this); + + if (blackbox_attrib.workspace >= screen->getWorkspaceCount()) + screen->getCurrentWorkspace()->addWindow(this, place_window); + else + screen->getWorkspace(blackbox_attrib.workspace)-> + addWindow(this, place_window); + + if (! place_window) { + // don't need to call configure if we are letting the workspace + // place the window + configure(frame.rect.x(), frame.rect.y(), + frame.rect.width(), frame.rect.height()); + + } + + positionWindows(); + + XUngrabServer(otk::OBDisplay::display); + +#ifdef SHAPE + if (blackbox->hasShapeExtensions() && flags.shaped) + configureShape(); +#endif // SHAPE + + // now that we know where to put the window and what it should look like + // we apply the decorations + decorate(); + + grabButtons(); + + XMapSubwindows(otk::OBDisplay::display, frame.window); + + // this ensures the title, buttons, and other decor are properly displayed + redrawWindowFrame(); + + // preserve the window's initial state on first map, and its current state + // across a restart + unsigned long initial_state = current_state; + if (! getState()) + current_state = initial_state; + + // get sticky state from our parent window if we've got one + if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul && + client.transient_for->isStuck() != flags.stuck) + flags.stuck = True; + + if (flags.shaded) { + flags.shaded = False; + initial_state = current_state; + shade(); + + /* + At this point in the life of a window, current_state should only be set + to IconicState if the window was an *icon*, not if it was shaded. + */ + if (initial_state != IconicState) + current_state = NormalState; + } + + if (flags.stuck) { + flags.stuck = False; + stick(); + } + + if (flags.maximized && (functions & Func_Maximize)) + remaximize(); +} + + +BlackboxWindow::~BlackboxWindow(void) { +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n", + client.window); +#endif // DEBUG + + if (! timer) // window not managed... + return; + + if (flags.moving) + endMove(); + + screen->removeStrut(&client.strut); + screen->updateAvailableArea(); + + // We don't need to worry about resizing because resizing always grabs the X + // server. This should only ever happen if using opaque moving. + if (flags.moving) + endMove(); + + delete timer; + + if (client.window_group) { + BWindowGroup *group = blackbox->searchGroup(client.window_group); + if (group) group->removeWindow(this); + } + + // remove ourselves from our transient_for + if (isTransient()) { + if (client.transient_for != (BlackboxWindow *) ~0ul) + client.transient_for->client.transientList.remove(this); + client.transient_for = (BlackboxWindow*) 0; + } + + if (client.transientList.size() > 0) { + // reset transient_for for all transients + BlackboxWindowList::iterator it, end = client.transientList.end(); + for (it = client.transientList.begin(); it != end; ++it) + (*it)->client.transient_for = (BlackboxWindow*) 0; + } + + if (frame.title) + destroyTitlebar(); + + if (frame.handle) + destroyHandle(); + + if (frame.plate) { + blackbox->removeWindowSearch(frame.plate); + XDestroyWindow(otk::OBDisplay::display, frame.plate); + } + + if (frame.window) { + blackbox->removeWindowSearch(frame.window); + XDestroyWindow(otk::OBDisplay::display, frame.window); + } + + blackbox->removeWindowSearch(client.window); +} + + +void BlackboxWindow::enableDecor(bool enable) { + blackbox_attrib.flags |= AttribDecoration; + blackbox_attrib.decoration = enable ? DecorNormal : DecorNone; + setupDecor(); + + // we can not be shaded if we lack a titlebar + if (! (decorations & Decor_Titlebar) && flags.shaded) + shade(); + + if (flags.visible && frame.window) { + XMapSubwindows(otk::OBDisplay::display, frame.window); + XMapWindow(otk::OBDisplay::display, frame.window); + } + + reconfigure(); + setState(current_state); +} + + +void BlackboxWindow::setupDecor() { + if (blackbox_attrib.decoration != DecorNone) { + // start with everything on + decorations = Decor_Close | + (mwm_decorations & Decor_Titlebar ? Decor_Titlebar : 0) | + (mwm_decorations & Decor_Border ? Decor_Border : 0) | + (mwm_decorations & Decor_Handle ? Decor_Handle : 0) | + (mwm_decorations & Decor_Iconify ? Decor_Iconify : 0) | + (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0); + + if (! (functions & Func_Close)) decorations &= ~Decor_Close; + if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize; + if (! (functions & Func_Iconify)) decorations &= ~Decor_Iconify; + if (! (functions & Func_Resize)) decorations &= ~Decor_Handle; + + switch (window_type) { + case Type_Desktop: + case Type_Dock: + case Type_Menu: + case Type_Splash: + // none of these windows are decorated by the window manager at all + decorations = 0; + break; + + case Type_Toolbar: + case Type_Utility: + decorations &= ~(Decor_Border); + break; + + case Type_Dialog: + decorations &= ~Decor_Handle; + break; + + case Type_Normal: + break; + } + } else { + decorations = 0; + } +} + +/* + * Creates a new top level window, with a given location, size, and border + * width. + * Returns: the newly created window + */ +Window BlackboxWindow::createToplevelWindow(void) { + XSetWindowAttributes attrib_create; + unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap | + CWOverrideRedirect | CWEventMask; + + attrib_create.background_pixmap = None; + attrib_create.colormap = screen->getColormap(); + attrib_create.override_redirect = True; + attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | + ButtonPress; + /* + We catch button presses because other wise they get passed down to the + root window, which will then cause root menus to show when you click the + window's frame. + */ + + return XCreateWindow(otk::OBDisplay::display, screen->getRootWindow(), + 0, 0, 1, 1, frame.border_w, screen->getDepth(), + InputOutput, screen->getVisual(), create_mask, + &attrib_create); +} + + +/* + * Creates a child window, and optionally associates a given cursor with + * the new window. + */ +Window BlackboxWindow::createChildWindow(Window parent, + unsigned long event_mask, + Cursor cursor) { + XSetWindowAttributes attrib_create; + unsigned long create_mask = CWBackPixmap | CWBorderPixel | + CWEventMask; + + attrib_create.background_pixmap = None; + attrib_create.event_mask = event_mask; + + if (cursor) { + create_mask |= CWCursor; + attrib_create.cursor = cursor; + } + + return XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0, + screen->getDepth(), InputOutput, screen->getVisual(), + create_mask, &attrib_create); +} + + +void BlackboxWindow::associateClientWindow(void) { + XSetWindowBorderWidth(otk::OBDisplay::display, client.window, 0); + getWMName(); + getWMIconName(); + + XChangeSaveSet(otk::OBDisplay::display, client.window, SetModeInsert); + + XSelectInput(otk::OBDisplay::display, frame.plate, SubstructureRedirectMask); + + /* + note we used to grab around this call to XReparentWindow however the + server is now grabbed before this method is called + */ + unsigned long event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + XSelectInput(otk::OBDisplay::display, client.window, + event_mask & ~StructureNotifyMask); + XReparentWindow(otk::OBDisplay::display, client.window, frame.plate, 0, 0); + XSelectInput(otk::OBDisplay::display, client.window, event_mask); + + XRaiseWindow(otk::OBDisplay::display, frame.plate); + XMapSubwindows(otk::OBDisplay::display, frame.plate); + +#ifdef SHAPE + if (blackbox->hasShapeExtensions()) { + XShapeSelectInput(otk::OBDisplay::display, client.window, + ShapeNotifyMask); + + Bool shaped = False; + int foo; + unsigned int ufoo; + + XShapeQueryExtents(otk::OBDisplay::display, client.window, &shaped, + &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo, + &ufoo, &ufoo); + flags.shaped = shaped; + } +#endif // SHAPE +} + + +void BlackboxWindow::decorate(void) { + otk::BTexture* texture; + + texture = &(screen->getWindowStyle()->b_focus); + frame.fbutton = texture->render(frame.button_w, frame.button_w, + frame.fbutton); + if (! frame.fbutton) + frame.fbutton_pixel = texture->color().pixel(); + + texture = &(screen->getWindowStyle()->b_unfocus); + frame.ubutton = texture->render(frame.button_w, frame.button_w, + frame.ubutton); + if (! frame.ubutton) + frame.ubutton_pixel = texture->color().pixel(); + + unsigned char needsPressed = 0; + + texture = &(screen->getWindowStyle()->b_pressed_focus); + + if (texture->texture() != otk::BTexture::NoTexture) { + frame.pfbutton = texture->render(frame.button_w, frame.button_w, + frame.pfbutton); + if (! frame.pfbutton) + frame.pfbutton_pixel = texture->color().pixel(); + } else { + needsPressed = 0x1; + } + + texture = &(screen->getWindowStyle()->b_pressed_unfocus); + + if (texture->texture() != otk::BTexture::NoTexture) { + frame.pubutton = texture->render(frame.button_w, frame.button_w, + frame.pubutton); + if (! frame.pubutton) + frame.pubutton = texture->color().pixel(); + } else { + needsPressed |= 0x2; + } + + // if we either pressed unfocused, or pressed focused were undefined, + // make them inherit from the old resource. It's a hack for sure, but + // it allows for some backwards and forwards compatibility. + if (needsPressed) { + texture = &(screen->getWindowStyle()->b_pressed); + + if (needsPressed & 0x1) { + frame.pfbutton = texture->render(frame.button_w, frame.button_w, + frame.pfbutton); + if (! frame.pfbutton) + frame.pfbutton_pixel = texture->color().pixel(); + } + if (needsPressed & 0x2) { + frame.pubutton = texture->render(frame.button_w, frame.button_w, + frame.pubutton); + if (! frame.pubutton) + frame.pubutton = texture->color().pixel(); + } + + } + + if (decorations & Decor_Titlebar) { + texture = &(screen->getWindowStyle()->t_focus); + frame.ftitle = texture->render(frame.inside_w, frame.title_h, + frame.ftitle); + if (! frame.ftitle) + frame.ftitle_pixel = texture->color().pixel(); + + texture = &(screen->getWindowStyle()->t_unfocus); + frame.utitle = texture->render(frame.inside_w, frame.title_h, + frame.utitle); + if (! frame.utitle) + frame.utitle_pixel = texture->color().pixel(); + + XSetWindowBorder(otk::OBDisplay::display, frame.title, + screen->getBorderColor()->pixel()); + + decorateLabel(); + } + + if (decorations & Decor_Border) { + frame.fborder_pixel = screen->getWindowStyle()->f_focus.color().pixel(); + frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.color().pixel(); + } + + if (decorations & Decor_Handle) { + texture = &(screen->getWindowStyle()->h_focus); + frame.fhandle = texture->render(frame.inside_w, frame.handle_h, + frame.fhandle); + if (! frame.fhandle) + frame.fhandle_pixel = texture->color().pixel(); + + texture = &(screen->getWindowStyle()->h_unfocus); + frame.uhandle = texture->render(frame.inside_w, frame.handle_h, + frame.uhandle); + if (! frame.uhandle) + frame.uhandle_pixel = texture->color().pixel(); + + texture = &(screen->getWindowStyle()->g_focus); + frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip); + if (! frame.fgrip) + frame.fgrip_pixel = texture->color().pixel(); + + texture = &(screen->getWindowStyle()->g_unfocus); + frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip); + if (! frame.ugrip) + frame.ugrip_pixel = texture->color().pixel(); + + XSetWindowBorder(otk::OBDisplay::display, frame.handle, + screen->getBorderColor()->pixel()); + XSetWindowBorder(otk::OBDisplay::display, frame.left_grip, + screen->getBorderColor()->pixel()); + XSetWindowBorder(otk::OBDisplay::display, frame.right_grip, + screen->getBorderColor()->pixel()); + } + + XSetWindowBorder(otk::OBDisplay::display, frame.window, + screen->getBorderColor()->pixel()); +} + + +void BlackboxWindow::decorateLabel(void) { + otk::BTexture *texture; + + texture = &(screen->getWindowStyle()->l_focus); + frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel); + if (! frame.flabel) + frame.flabel_pixel = texture->color().pixel(); + + texture = &(screen->getWindowStyle()->l_unfocus); + frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel); + if (! frame.ulabel) + frame.ulabel_pixel = texture->color().pixel(); +} + + +void BlackboxWindow::createHandle(void) { + frame.handle = createChildWindow(frame.window, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + blackbox->saveWindowSearch(frame.handle, this); + + frame.left_grip = + createChildWindow(frame.handle, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask, + blackbox->getLowerLeftAngleCursor()); + blackbox->saveWindowSearch(frame.left_grip, this); + + frame.right_grip = + createChildWindow(frame.handle, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask, + blackbox->getLowerRightAngleCursor()); + blackbox->saveWindowSearch(frame.right_grip, this); +} + + +void BlackboxWindow::destroyHandle(void) { + if (frame.fhandle) + screen->getImageControl()->removeImage(frame.fhandle); + + if (frame.uhandle) + screen->getImageControl()->removeImage(frame.uhandle); + + if (frame.fgrip) + screen->getImageControl()->removeImage(frame.fgrip); + + if (frame.ugrip) + screen->getImageControl()->removeImage(frame.ugrip); + + blackbox->removeWindowSearch(frame.left_grip); + blackbox->removeWindowSearch(frame.right_grip); + + XDestroyWindow(otk::OBDisplay::display, frame.left_grip); + XDestroyWindow(otk::OBDisplay::display, frame.right_grip); + frame.left_grip = frame.right_grip = None; + + blackbox->removeWindowSearch(frame.handle); + XDestroyWindow(otk::OBDisplay::display, frame.handle); + frame.handle = None; +} + + +void BlackboxWindow::createTitlebar(void) { + frame.title = createChildWindow(frame.window, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + frame.label = createChildWindow(frame.title, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + blackbox->saveWindowSearch(frame.title, this); + blackbox->saveWindowSearch(frame.label, this); + + if (decorations & Decor_Iconify) createIconifyButton(); + if (decorations & Decor_Maximize) createMaximizeButton(); + if (decorations & Decor_Close) createCloseButton(); +} + + +void BlackboxWindow::destroyTitlebar(void) { + if (frame.close_button) + destroyCloseButton(); + + if (frame.iconify_button) + destroyIconifyButton(); + + if (frame.maximize_button) + destroyMaximizeButton(); + + if (frame.stick_button) + destroyStickyButton(); + + if (frame.ftitle) + screen->getImageControl()->removeImage(frame.ftitle); + + if (frame.utitle) + screen->getImageControl()->removeImage(frame.utitle); + + if (frame.flabel) + screen->getImageControl()->removeImage(frame.flabel); + + if( frame.ulabel) + screen->getImageControl()->removeImage(frame.ulabel); + + if (frame.fbutton) + screen->getImageControl()->removeImage(frame.fbutton); + + if (frame.ubutton) + screen->getImageControl()->removeImage(frame.ubutton); + + blackbox->removeWindowSearch(frame.title); + blackbox->removeWindowSearch(frame.label); + + XDestroyWindow(otk::OBDisplay::display, frame.label); + XDestroyWindow(otk::OBDisplay::display, frame.title); + frame.title = frame.label = None; +} + + +void BlackboxWindow::createCloseButton(void) { + if (frame.title != None) { + frame.close_button = createChildWindow(frame.title, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + blackbox->saveWindowSearch(frame.close_button, this); + } +} + + +void BlackboxWindow::destroyCloseButton(void) { + blackbox->removeWindowSearch(frame.close_button); + XDestroyWindow(otk::OBDisplay::display, frame.close_button); + frame.close_button = None; +} + + +void BlackboxWindow::createIconifyButton(void) { + if (frame.title != None) { + frame.iconify_button = createChildWindow(frame.title, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + blackbox->saveWindowSearch(frame.iconify_button, this); + } +} + + +void BlackboxWindow::destroyIconifyButton(void) { + blackbox->removeWindowSearch(frame.iconify_button); + XDestroyWindow(otk::OBDisplay::display, frame.iconify_button); + frame.iconify_button = None; +} + + +void BlackboxWindow::createMaximizeButton(void) { + if (frame.title != None) { + frame.maximize_button = createChildWindow(frame.title, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + blackbox->saveWindowSearch(frame.maximize_button, this); + } +} + + +void BlackboxWindow::destroyMaximizeButton(void) { + blackbox->removeWindowSearch(frame.maximize_button); + XDestroyWindow(otk::OBDisplay::display, frame.maximize_button); + frame.maximize_button = None; +} + +void BlackboxWindow::createStickyButton(void) { + if (frame.title != None) { + frame.stick_button = createChildWindow(frame.title, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + blackbox->saveWindowSearch(frame.stick_button, this); + } +} + +void BlackboxWindow::destroyStickyButton(void) { + blackbox->removeWindowSearch(frame.stick_button); + XDestroyWindow(otk::OBDisplay::display, frame.stick_button); + frame.stick_button = None; +} + +void BlackboxWindow::positionButtons(bool redecorate_label) { + string layout = blackbox->getTitlebarLayout(); + string parsed; + + bool hasclose, hasiconify, hasmaximize, haslabel, hasstick; + hasclose = hasiconify = hasmaximize = haslabel = hasstick = false; + + string::const_iterator it, end; + for (it = layout.begin(), end = layout.end(); it != end; ++it) { + switch(*it) { + case 'C': + if (! hasclose && (decorations & Decor_Close)) { + hasclose = true; + parsed += *it; + } + break; + case 'I': + if (! hasiconify && (decorations & Decor_Iconify)) { + hasiconify = true; + parsed += *it; + } + break; + case 'S': + if (!hasstick) { + hasstick = true; + parsed += *it; + } + break; + case 'M': + if (! hasmaximize && (decorations & Decor_Maximize)) { + hasmaximize = true; + parsed += *it; + } + break; + case 'L': + if (! haslabel) { + haslabel = true; + parsed += *it; + } + break; + } + } + + if (! hasclose && frame.close_button) + destroyCloseButton(); + if (! hasiconify && frame.iconify_button) + destroyIconifyButton(); + if (! hasmaximize && frame.maximize_button) + destroyMaximizeButton(); + if (! hasstick && frame.stick_button) + destroyStickyButton(); + if (! haslabel) + parsed += 'L'; // require that the label be in the layout + + const unsigned int bsep = frame.bevel_w + 1; // separation between elements + const unsigned int by = frame.bevel_w + 1; + const unsigned int ty = frame.bevel_w; + + frame.label_w = frame.inside_w - bsep * 2 - + (frame.button_w + bsep) * (parsed.size() - 1); + + unsigned int x = bsep; + for (it = parsed.begin(), end = parsed.end(); it != end; ++it) { + switch(*it) { + case 'C': + if (! frame.close_button) createCloseButton(); + XMoveResizeWindow(otk::OBDisplay::display, frame.close_button, x, by, + frame.button_w, frame.button_w); + x += frame.button_w + bsep; + break; + case 'I': + if (! frame.iconify_button) createIconifyButton(); + XMoveResizeWindow(otk::OBDisplay::display, frame.iconify_button, x, by, + frame.button_w, frame.button_w); + x += frame.button_w + bsep; + break; + case 'S': + if (! frame.stick_button) createStickyButton(); + XMoveResizeWindow(otk::OBDisplay::display, frame.stick_button, x, by, + frame.button_w, frame.button_w); + x += frame.button_w + bsep; + break; + case 'M': + if (! frame.maximize_button) createMaximizeButton(); + XMoveResizeWindow(otk::OBDisplay::display, frame.maximize_button, x, by, + frame.button_w, frame.button_w); + x += frame.button_w + bsep; + break; + case 'L': + XMoveResizeWindow(otk::OBDisplay::display, frame.label, x, ty, + frame.label_w, frame.label_h); + x += frame.label_w + bsep; + break; + } + } + + if (redecorate_label) decorateLabel(); + redrawLabel(); + redrawAllButtons(); +} + + +void BlackboxWindow::reconfigure(void) { + restoreGravity(client.rect); + upsize(); + applyGravity(frame.rect); + positionWindows(); + decorate(); + redrawWindowFrame(); + + ungrabButtons(); + grabButtons(); +} + + +void BlackboxWindow::grabButtons(void) { + mod_mask = blackbox->getMouseModMask(); + + if (! screen->isSloppyFocus() || screen->doClickRaise()) + // grab button 1 for changing focus/raising + otk::OBDisplay::grabButton(Button1, 0, frame.plate, True, ButtonPressMask, + GrabModeSync, GrabModeSync, frame.plate, None, + screen->allowScrollLock()); + + if (functions & Func_Move) + otk::OBDisplay::grabButton(Button1, mod_mask, frame.window, True, + ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, + GrabModeAsync, frame.window, None, + screen->allowScrollLock()); + if (functions & Func_Resize) + otk::OBDisplay::grabButton(Button3, mod_mask, frame.window, True, + ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, + GrabModeAsync, frame.window, None, + screen->allowScrollLock()); + // alt+middle lowers the window + otk::OBDisplay::grabButton(Button2, mod_mask, frame.window, True, + ButtonReleaseMask, GrabModeAsync, GrabModeAsync, + frame.window, None, screen->allowScrollLock()); +} + + +void BlackboxWindow::ungrabButtons(void) { + otk::OBDisplay::ungrabButton(Button1, 0, frame.plate); + otk::OBDisplay::ungrabButton(Button1, mod_mask, frame.window); + otk::OBDisplay::ungrabButton(Button2, mod_mask, frame.window); + otk::OBDisplay::ungrabButton(Button3, mod_mask, frame.window); +} + + +void BlackboxWindow::positionWindows(void) { + XMoveResizeWindow(otk::OBDisplay::display, frame.window, + frame.rect.x(), frame.rect.y(), frame.inside_w, + (flags.shaded) ? frame.title_h : frame.inside_h); + XSetWindowBorderWidth(otk::OBDisplay::display, frame.window, + frame.border_w); + XSetWindowBorderWidth(otk::OBDisplay::display, frame.plate, + frame.mwm_border_w); + XMoveResizeWindow(otk::OBDisplay::display, frame.plate, + frame.margin.left - frame.mwm_border_w - frame.border_w, + frame.margin.top - frame.mwm_border_w - frame.border_w, + client.rect.width(), client.rect.height()); + XMoveResizeWindow(otk::OBDisplay::display, client.window, + 0, 0, client.rect.width(), client.rect.height()); + // ensure client.rect contains the real location + client.rect.setPos(frame.rect.left() + frame.margin.left, + frame.rect.top() + frame.margin.top); + + if (decorations & Decor_Titlebar) { + if (frame.title == None) createTitlebar(); + + XSetWindowBorderWidth(otk::OBDisplay::display, frame.title, + frame.border_w); + XMoveResizeWindow(otk::OBDisplay::display, frame.title, -frame.border_w, + -frame.border_w, frame.inside_w, frame.title_h); + + positionButtons(); + XMapSubwindows(otk::OBDisplay::display, frame.title); + XMapWindow(otk::OBDisplay::display, frame.title); + } else if (frame.title) { + destroyTitlebar(); + } + if (decorations & Decor_Handle) { + if (frame.handle == None) createHandle(); + XSetWindowBorderWidth(otk::OBDisplay::display, frame.handle, + frame.border_w); + XSetWindowBorderWidth(otk::OBDisplay::display, frame.left_grip, + frame.border_w); + XSetWindowBorderWidth(otk::OBDisplay::display, frame.right_grip, + frame.border_w); + + // use client.rect here so the value is correct even if shaded + XMoveResizeWindow(otk::OBDisplay::display, frame.handle, + -frame.border_w, + client.rect.height() + frame.margin.top + + frame.mwm_border_w - frame.border_w, + frame.inside_w, frame.handle_h); + XMoveResizeWindow(otk::OBDisplay::display, frame.left_grip, + -frame.border_w, -frame.border_w, + frame.grip_w, frame.handle_h); + XMoveResizeWindow(otk::OBDisplay::display, frame.right_grip, + frame.inside_w - frame.grip_w - frame.border_w, + -frame.border_w, frame.grip_w, frame.handle_h); + + XMapSubwindows(otk::OBDisplay::display, frame.handle); + XMapWindow(otk::OBDisplay::display, frame.handle); + } else if (frame.handle) { + destroyHandle(); + } + XSync(otk::OBDisplay::display, False); +} + + +void BlackboxWindow::updateStrut(void) { + unsigned long num = 4; + unsigned long *data; + if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal, + num, &data)) + return; + + if (num == 4) { + client.strut.left = data[0]; + client.strut.right = data[1]; + client.strut.top = data[2]; + client.strut.bottom = data[3]; + + screen->updateAvailableArea(); + } + + delete [] data; +} + + +bool BlackboxWindow::getWindowType(void) { + window_type = (WindowType) -1; + + unsigned long *val; + unsigned long num = (unsigned) -1; + if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom, + num, &val)) { + for (unsigned long i = 0; i < num; ++i) { + if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_desktop)) + window_type = Type_Desktop; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dock)) + window_type = Type_Dock; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_toolbar)) + window_type = Type_Toolbar; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_menu)) + window_type = Type_Menu; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_utility)) + window_type = Type_Utility; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_splash)) + window_type = Type_Splash; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dialog)) + window_type = Type_Dialog; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_normal)) + window_type = Type_Normal; + else if (val[i] == + xatom->getAtom(XAtom::kde_net_wm_window_type_override)) + mwm_decorations = 0; // prevent this window from getting any decor + } + delete val; + } + + if (window_type == (WindowType) -1) { + /* + * 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. + */ + if (isTransient()) + window_type = Type_Dialog; + else + window_type = Type_Normal; + + return False; + } + + return True; +} + + +void BlackboxWindow::getWMName(void) { + if (xatom->getValue(client.window, XAtom::net_wm_name, + XAtom::utf8, client.title) && + !client.title.empty()) { + xatom->eraseValue(client.window, XAtom::net_wm_visible_name); + return; + } + //fall through to using WM_NAME + if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title) + && !client.title.empty()) { + xatom->eraseValue(client.window, XAtom::net_wm_visible_name); + return; + } + // fall back to an internal default + client.title = "Unnamed"; + xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8, + client.title); + +#ifdef DEBUG_WITH_ID + // the 16 is the 8 chars of the debug text plus the number + char *tmp = new char[client.title.length() + 16]; + sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window); + client.title = tmp; + delete tmp; +#endif +} + + +void BlackboxWindow::getWMIconName(void) { + if (xatom->getValue(client.window, XAtom::net_wm_icon_name, + XAtom::utf8, client.icon_title) && + !client.icon_title.empty()) { + xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name); + return; + } + //fall through to using WM_ICON_NAME + if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi, + client.icon_title) && + !client.icon_title.empty()) { + xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name); + return; + } + // fall back to using the main name + client.icon_title = client.title; + xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8, + client.icon_title); +} + + +/* + * Retrieve which WM Protocols are supported by the client window. + * If the WM_DELETE_WINDOW protocol is supported, add the close button to the + * window's decorations and allow the close behavior. + * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates + * this. + */ +void BlackboxWindow::getWMProtocols(void) { + Atom *proto; + int num_return = 0; + + if (XGetWMProtocols(otk::OBDisplay::display, client.window, + &proto, &num_return)) { + for (int i = 0; i < num_return; ++i) { + if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) { + decorations |= Decor_Close; + functions |= Func_Close; + } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus)) + flags.send_focus_message = True; + } + + XFree(proto); + } +} + + +/* + * Gets the value of the WM_HINTS property. + * If the property is not set, then use a set of default values. + */ +void BlackboxWindow::getWMHints(void) { + focus_mode = F_Passive; + + // remove from current window group + if (client.window_group) { + BWindowGroup *group = blackbox->searchGroup(client.window_group); + if (group) group->removeWindow(this); + } + client.window_group = None; + + XWMHints *wmhint = XGetWMHints(otk::OBDisplay::display, client.window); + if (! wmhint) { + return; + } + + if (wmhint->flags & InputHint) { + if (wmhint->input == True) { + if (flags.send_focus_message) + focus_mode = F_LocallyActive; + } else { + if (flags.send_focus_message) + focus_mode = F_GloballyActive; + else + focus_mode = F_NoInput; + } + } + + if (wmhint->flags & StateHint) + current_state = wmhint->initial_state; + + if (wmhint->flags & WindowGroupHint) { + client.window_group = wmhint->window_group; + + // add window to the appropriate group + BWindowGroup *group = blackbox->searchGroup(client.window_group); + if (! group) { // no group found, create it! + new BWindowGroup(blackbox, client.window_group); + group = blackbox->searchGroup(client.window_group); + } + if (group) + group->addWindow(this); + } + + XFree(wmhint); +} + + +/* + * Gets the value of the WM_NORMAL_HINTS property. + * If the property is not set, then use a set of default values. + */ +void BlackboxWindow::getWMNormalHints(void) { + long icccm_mask; + XSizeHints sizehint; + + client.min_width = client.min_height = + client.width_inc = client.height_inc = 1; + client.base_width = client.base_height = 0; + client.win_gravity = NorthWestGravity; +#if 0 + client.min_aspect_x = client.min_aspect_y = + client.max_aspect_x = client.max_aspect_y = 1; +#endif + + // don't limit the size of a window, the default max width is the biggest + // possible + client.max_width = (unsigned) -1; + client.max_height = (unsigned) -1; + + + if (! XGetWMNormalHints(otk::OBDisplay::display, client.window, + &sizehint, &icccm_mask)) + return; + + client.normal_hint_flags = sizehint.flags; + + if (sizehint.flags & PMinSize) { + if (sizehint.min_width >= 0) + client.min_width = sizehint.min_width; + if (sizehint.min_height >= 0) + client.min_height = sizehint.min_height; + } + + if (sizehint.flags & PMaxSize) { + if (sizehint.max_width > static_cast(client.min_width)) + client.max_width = sizehint.max_width; + else + client.max_width = client.min_width; + + if (sizehint.max_height > static_cast(client.min_height)) + client.max_height = sizehint.max_height; + else + client.max_height = client.min_height; + } + + if (sizehint.flags & PResizeInc) { + client.width_inc = sizehint.width_inc; + client.height_inc = sizehint.height_inc; + } + +#if 0 // we do not support this at the moment + if (sizehint.flags & PAspect) { + client.min_aspect_x = sizehint.min_aspect.x; + client.min_aspect_y = sizehint.min_aspect.y; + client.max_aspect_x = sizehint.max_aspect.x; + client.max_aspect_y = sizehint.max_aspect.y; + } +#endif + + if (sizehint.flags & PBaseSize) { + client.base_width = sizehint.base_width; + client.base_height = sizehint.base_height; + } + + if (sizehint.flags & PWinGravity) + client.win_gravity = sizehint.win_gravity; +} + + +/* + * Gets the NETWM hints for the class' contained window. + */ +void BlackboxWindow::getNetWMHints(void) { + unsigned long workspace; + + if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, + workspace)) { + if (workspace == 0xffffffff) + flags.stuck = True; + else + blackbox_attrib.workspace = workspace; + } + + unsigned long *state; + unsigned long num = (unsigned) -1; + if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom, + num, &state)) { + bool vert = False, + horz = False; + for (unsigned long i = 0; i < num; ++i) { + if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) + flags.modal = True; + else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded)) + flags.shaded = True; + else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) + flags.skip_taskbar = True; + else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager)) + flags.skip_pager = True; + else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen)) + flags.fullscreen = True; + else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden)) + setState(IconicState); + else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert)) + vert = True; + else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz)) + horz = True; + } + if (vert && horz) + flags.maximized = 1; + else if (vert) + flags.maximized = 2; + else if (horz) + flags.maximized = 3; + + delete [] state; + } +} + + +/* + * Gets the MWM hints for the class' contained window. + * This is used while initializing the window to its first state, and not + * thereafter. + * Returns: true if the MWM hints are successfully retreived and applied; + * false if they are not. + */ +void BlackboxWindow::getMWMHints(void) { + unsigned long num; + MwmHints *mwm_hint; + + num = PropMwmHintsElements; + if (! xatom->getValue(client.window, XAtom::motif_wm_hints, + XAtom::motif_wm_hints, num, + (unsigned long **)&mwm_hint)) + return; + if (num < PropMwmHintsElements) { + delete [] mwm_hint; + return; + } + + if (mwm_hint->flags & MwmHintsDecorations) { + if (mwm_hint->decorations & MwmDecorAll) { + mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border | + Decor_Iconify | Decor_Maximize; + } else { + mwm_decorations = 0; + + if (mwm_hint->decorations & MwmDecorBorder) + mwm_decorations |= Decor_Border; + if (mwm_hint->decorations & MwmDecorHandle) + mwm_decorations |= Decor_Handle; + if (mwm_hint->decorations & MwmDecorTitle) + mwm_decorations |= Decor_Titlebar; + if (mwm_hint->decorations & MwmDecorIconify) + mwm_decorations |= Decor_Iconify; + if (mwm_hint->decorations & MwmDecorMaximize) + mwm_decorations |= Decor_Maximize; + } + } + + if (mwm_hint->flags & MwmHintsFunctions) { + if (mwm_hint->functions & MwmFuncAll) { + functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize | + Func_Close; + } else { + functions = 0; + + if (mwm_hint->functions & MwmFuncResize) + functions |= Func_Resize; + if (mwm_hint->functions & MwmFuncMove) + functions |= Func_Move; + if (mwm_hint->functions & MwmFuncIconify) + functions |= Func_Iconify; + if (mwm_hint->functions & MwmFuncMaximize) + functions |= Func_Maximize; + if (mwm_hint->functions & MwmFuncClose) + functions |= Func_Close; + } + } + delete [] mwm_hint; +} + + +/* + * Gets the blackbox hints from the class' contained window. + * This is used while initializing the window to its first state, and not + * thereafter. + * Returns: true if the hints are successfully retreived and applied; false if + * they are not. + */ +bool BlackboxWindow::getBlackboxHints(void) { + unsigned long num; + BlackboxHints *blackbox_hint; + + num = PropBlackboxHintsElements; + if (! xatom->getValue(client.window, XAtom::blackbox_hints, + XAtom::blackbox_hints, num, + (unsigned long **)&blackbox_hint)) + return False; + if (num < PropBlackboxHintsElements) { + delete [] blackbox_hint; + return False; + } + + if (blackbox_hint->flags & AttribShaded) + flags.shaded = (blackbox_hint->attrib & AttribShaded); + + if ((blackbox_hint->flags & AttribMaxHoriz) && + (blackbox_hint->flags & AttribMaxVert)) + flags.maximized = (blackbox_hint->attrib & + (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0; + else if (blackbox_hint->flags & AttribMaxVert) + flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0; + else if (blackbox_hint->flags & AttribMaxHoriz) + flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0; + + if (blackbox_hint->flags & AttribOmnipresent) + flags.stuck = (blackbox_hint->attrib & AttribOmnipresent); + + if (blackbox_hint->flags & AttribWorkspace) + blackbox_attrib.workspace = blackbox_hint->workspace; + + // if (blackbox_hint->flags & AttribStack) + // don't yet have always on top/bottom for blackbox yet... working + // on that + + if (blackbox_hint->flags & AttribDecoration) { + switch (blackbox_hint->decoration) { + case DecorNone: + blackbox_attrib.decoration = DecorNone; + break; + + case DecorTiny: + case DecorTool: + case DecorNormal: + default: + // blackbox_attrib.decoration defaults to DecorNormal + break; + } + } + + delete [] blackbox_hint; + + return True; +} + + +void BlackboxWindow::getTransientInfo(void) { + if (client.transient_for && + client.transient_for != (BlackboxWindow *) ~0ul) { + // reset transient_for in preparation of looking for a new owner + client.transient_for->client.transientList.remove(this); + } + + // we have no transient_for until we find a new one + client.transient_for = (BlackboxWindow *) 0; + + Window trans_for; + if (! XGetTransientForHint(otk::OBDisplay::display, client.window, + &trans_for)) { + // transient_for hint not set + return; + } + + if (trans_for == client.window) { + // wierd client... treat this window as a normal window + return; + } + + if (trans_for == None || trans_for == screen->getRootWindow()) { + // this is an undocumented interpretation of the ICCCM. a transient + // associated with None/Root/itself is assumed to be a modal root + // transient. we don't support the concept of a global transient, + // so we just associate this transient with nothing, and perhaps + // we will add support later for global modality. + client.transient_for = (BlackboxWindow *) ~0ul; + flags.modal = True; + return; + } + + client.transient_for = blackbox->searchWindow(trans_for); + if (! client.transient_for && + client.window_group && trans_for == client.window_group) { + // no direct transient_for, perhaps this is a group transient? + BWindowGroup *group = blackbox->searchGroup(client.window_group); + if (group) client.transient_for = group->find(screen); + } + + if (! client.transient_for || client.transient_for == this) { + // no transient_for found, or we have a wierd client that wants to be + // a transient for itself, so we treat this window as a normal window + client.transient_for = (BlackboxWindow*) 0; + return; + } + + // Check for a circular transient state: this can lock up Blackbox + // when it tries to find the non-transient window for a transient. + BlackboxWindow *w = this; + while(w->client.transient_for && + w->client.transient_for != (BlackboxWindow *) ~0ul) { + if(w->client.transient_for == this) { + client.transient_for = (BlackboxWindow*) 0; + break; + } + w = w->client.transient_for; + } + + if (client.transient_for && + client.transient_for != (BlackboxWindow *) ~0ul) { + // register ourselves with our new transient_for + client.transient_for->client.transientList.push_back(this); + flags.stuck = client.transient_for->flags.stuck; + } +} + + +BlackboxWindow *BlackboxWindow::getTransientFor(void) const { + if (client.transient_for && + client.transient_for != (BlackboxWindow*) ~0ul) + return client.transient_for; + return 0; +} + + +/* + * This function is responsible for updating both the client and the frame + * rectangles. + * According to the ICCCM a client message is not sent for a resize, only a + * move. + */ +void BlackboxWindow::configure(int dx, int dy, + unsigned int dw, unsigned int dh) { + bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) && + ! flags.moving); + + if (dw != frame.rect.width() || dh != frame.rect.height()) { + frame.rect.setRect(dx, dy, dw, dh); + frame.inside_w = frame.rect.width() - (frame.border_w * 2); + frame.inside_h = frame.rect.height() - (frame.border_w * 2); + + if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0) + frame.rect.setPos(0, 0); + + client.rect.setCoords(frame.rect.left() + frame.margin.left, + frame.rect.top() + frame.margin.top, + frame.rect.right() - frame.margin.right, + frame.rect.bottom() - frame.margin.bottom); + +#ifdef SHAPE + if (blackbox->hasShapeExtensions() && flags.shaped) { + configureShape(); + } +#endif // SHAPE + + positionWindows(); + decorate(); + redrawWindowFrame(); + } else { + frame.rect.setPos(dx, dy); + + XMoveWindow(otk::OBDisplay::display, frame.window, + frame.rect.x(), frame.rect.y()); + /* + we may have been called just after an opaque window move, so even though + the old coords match the new ones no ConfigureNotify has been sent yet. + There are likely other times when this will be relevant as well. + */ + if (! flags.moving) send_event = True; + } + + if (send_event) { + // if moving, the update and event will occur when the move finishes + client.rect.setPos(frame.rect.left() + frame.margin.left, + frame.rect.top() + frame.margin.top); + + XEvent event; + event.type = ConfigureNotify; + + event.xconfigure.display = otk::OBDisplay::display; + event.xconfigure.event = client.window; + event.xconfigure.window = client.window; + event.xconfigure.x = client.rect.x(); + event.xconfigure.y = client.rect.y(); + event.xconfigure.width = client.rect.width(); + event.xconfigure.height = client.rect.height(); + event.xconfigure.border_width = client.old_bw; + event.xconfigure.above = frame.window; + event.xconfigure.override_redirect = False; + + XSendEvent(otk::OBDisplay::display, client.window, False, + StructureNotifyMask, &event); + XFlush(otk::OBDisplay::display); + } +} + + +#ifdef SHAPE +void BlackboxWindow::configureShape(void) { + XShapeCombineShape(otk::OBDisplay::display, frame.window, ShapeBounding, + frame.margin.left - frame.border_w, + frame.margin.top - frame.border_w, + client.window, ShapeBounding, ShapeSet); + + int num = 0; + XRectangle xrect[2]; + + if (decorations & Decor_Titlebar) { + xrect[0].x = xrect[0].y = -frame.border_w; + xrect[0].width = frame.rect.width(); + xrect[0].height = frame.title_h + (frame.border_w * 2); + ++num; + } + + if (decorations & Decor_Handle) { + xrect[1].x = -frame.border_w; + xrect[1].y = frame.rect.height() - frame.margin.bottom + + frame.mwm_border_w - frame.border_w; + xrect[1].width = frame.rect.width(); + xrect[1].height = frame.handle_h + (frame.border_w * 2); + ++num; + } + + XShapeCombineRectangles(otk::OBDisplay::display, frame.window, + ShapeBounding, 0, 0, xrect, num, + ShapeUnion, Unsorted); +} + + +void BlackboxWindow::clearShape(void) { + XShapeCombineMask(otk::OBDisplay::display, frame.window, ShapeBounding, + frame.margin.left - frame.border_w, + frame.margin.top - frame.border_w, + None, ShapeSet); +} +#endif // SHAPE + + +bool BlackboxWindow::setInputFocus(void) { + if (flags.focused) return True; + + assert(flags.stuck || // window must be on the current workspace or sticky + blackbox_attrib.workspace == screen->getCurrentWorkspaceID()); + + /* + We only do this check for normal windows and dialogs because other windows + do this on purpose, such as kde's kicker, and we don't want to go moving + it. + */ + if (window_type == Type_Normal || window_type == Type_Dialog) + if (! frame.rect.intersects(screen->getRect())) { + // client is outside the screen, move it to the center + configure((screen->getWidth() - frame.rect.width()) / 2, + (screen->getHeight() - frame.rect.height()) / 2, + frame.rect.width(), frame.rect.height()); + } + + if (client.transientList.size() > 0) { + // transfer focus to any modal transients + BlackboxWindowList::iterator it, end = client.transientList.end(); + for (it = client.transientList.begin(); it != end; ++it) + if ((*it)->flags.modal) return (*it)->setInputFocus(); + } + + bool ret = True; + if (focus_mode == F_LocallyActive || focus_mode == F_Passive) { + XSetInputFocus(otk::OBDisplay::display, client.window, + RevertToPointerRoot, CurrentTime); + } else { + /* we could set the focus to none, since the window doesn't accept focus, + * but we shouldn't set focus to nothing since this would surely make + * someone angry + */ + ret = False; + } + + if (flags.send_focus_message) { + XEvent ce; + ce.xclient.type = ClientMessage; + ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols); + ce.xclient.display = otk::OBDisplay::display; + ce.xclient.window = client.window; + ce.xclient.format = 32; + ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus); + ce.xclient.data.l[1] = blackbox->getLastTime(); + ce.xclient.data.l[2] = 0l; + ce.xclient.data.l[3] = 0l; + ce.xclient.data.l[4] = 0l; + XSendEvent(otk::OBDisplay::display, client.window, False, + NoEventMask, &ce); + XFlush(otk::OBDisplay::display); + } + + return ret; +} + + +void BlackboxWindow::iconify(void) { + if (flags.iconic || ! (functions & Func_Iconify)) return; + + // We don't need to worry about resizing because resizing always grabs the X + // server. This should only ever happen if using opaque moving. + if (flags.moving) + endMove(); + + /* + * we don't want this XUnmapWindow call to generate an UnmapNotify event, so + * we need to clear the event mask on client.window for a split second. + * HOWEVER, since X11 is asynchronous, the window could be destroyed in that + * split second, leaving us with a ghost window... so, we need to do this + * while the X server is grabbed + */ + unsigned long event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + XGrabServer(otk::OBDisplay::display); + XSelectInput(otk::OBDisplay::display, client.window, + event_mask & ~StructureNotifyMask); + XUnmapWindow(otk::OBDisplay::display, client.window); + XSelectInput(otk::OBDisplay::display, client.window, event_mask); + XUngrabServer(otk::OBDisplay::display); + + XUnmapWindow(otk::OBDisplay::display, frame.window); + flags.visible = False; + flags.iconic = True; + + setState(IconicState); + + screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this); + if (flags.stuck) { + for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) + if (i != blackbox_attrib.workspace) + screen->getWorkspace(i)->removeWindow(this, True); + } + + if (isTransient()) { + if (client.transient_for != (BlackboxWindow *) ~0ul && + ! client.transient_for->flags.iconic) { + // iconify our transient_for + client.transient_for->iconify(); + } + } + + screen->addIcon(this); + + if (client.transientList.size() > 0) { + // iconify all transients + BlackboxWindowList::iterator it, end = client.transientList.end(); + for (it = client.transientList.begin(); it != end; ++it) { + if (! (*it)->flags.iconic) (*it)->iconify(); + } + } + screen->updateStackingList(); +} + + +void BlackboxWindow::show(void) { + flags.visible = True; + flags.iconic = False; + + current_state = (flags.shaded) ? IconicState : NormalState; + setState(current_state); + + XMapWindow(otk::OBDisplay::display, client.window); + XMapSubwindows(otk::OBDisplay::display, frame.window); + XMapWindow(otk::OBDisplay::display, frame.window); + +#if 0 + int real_x, real_y; + Window child; + XTranslateCoordinates(otk::OBDisplay::display, client.window, + screen->getRootWindow(), + 0, 0, &real_x, &real_y, &child); + fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(), + client.rect.left(), client.rect.top(), real_x, real_y); + assert(client.rect.left() == real_x && client.rect.top() == real_y); +#endif +} + + +void BlackboxWindow::deiconify(bool reassoc, bool raise) { + if (flags.iconic || reassoc) + screen->reassociateWindow(this, BSENTINEL, False); + else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) + return; + + show(); + + // reassociate and deiconify all transients + if (reassoc && client.transientList.size() > 0) { + BlackboxWindowList::iterator it, end = client.transientList.end(); + for (it = client.transientList.begin(); it != end; ++it) + (*it)->deiconify(True, False); + } + + if (raise) + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); +} + + +void BlackboxWindow::close(void) { + if (! (functions & Func_Close)) return; + + XEvent ce; + ce.xclient.type = ClientMessage; + ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols); + ce.xclient.display = otk::OBDisplay::display; + ce.xclient.window = client.window; + ce.xclient.format = 32; + ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window); + ce.xclient.data.l[1] = CurrentTime; + ce.xclient.data.l[2] = 0l; + ce.xclient.data.l[3] = 0l; + ce.xclient.data.l[4] = 0l; + XSendEvent(otk::OBDisplay::display, client.window, False, NoEventMask, &ce); + XFlush(otk::OBDisplay::display); +} + + +void BlackboxWindow::withdraw(void) { + // We don't need to worry about resizing because resizing always grabs the X + // server. This should only ever happen if using opaque moving. + if (flags.moving) + endMove(); + + flags.visible = False; + flags.iconic = False; + + setState(current_state); + + XUnmapWindow(otk::OBDisplay::display, frame.window); + + XGrabServer(otk::OBDisplay::display); + + unsigned long event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + XSelectInput(otk::OBDisplay::display, client.window, + event_mask & ~StructureNotifyMask); + XUnmapWindow(otk::OBDisplay::display, client.window); + XSelectInput(otk::OBDisplay::display, client.window, event_mask); + + XUngrabServer(otk::OBDisplay::display); +} + + +void BlackboxWindow::maximize(unsigned int button) { + if (! (functions & Func_Maximize)) return; + + // We don't need to worry about resizing because resizing always grabs the X + // server. This should only ever happen if using opaque moving. + if (flags.moving) + endMove(); + + if (flags.maximized) { + flags.maximized = 0; + + blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert); + blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert); + + /* + when a resize finishes, maximize(0) is called to clear any maximization + flags currently set. Otherwise it still thinks it is maximized. + so we do not need to call configure() because resizing will handle it + */ + if (! flags.resizing) + configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y, + blackbox_attrib.premax_w, blackbox_attrib.premax_h); + + blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0; + blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0; + + redrawAllButtons(); // in case it is not called in configure() + setState(current_state); + return; + } + + blackbox_attrib.premax_x = frame.rect.x(); + blackbox_attrib.premax_y = frame.rect.y(); + blackbox_attrib.premax_w = frame.rect.width(); + // use client.rect so that clients can be restored even if shaded + blackbox_attrib.premax_h = + client.rect.height() + frame.margin.top + frame.margin.bottom; + +#ifdef XINERAMA + if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) { + // find the area to use + RectList availableAreas = screen->allAvailableAreas(); + RectList::iterator it, end = availableAreas.end(); + + for (it = availableAreas.begin(); it != end; ++it) + if (it->intersects(frame.rect)) break; + if (it == end) // the window isn't inside an area + it = availableAreas.begin(); // so just default to the first one + + frame.changing = *it; + } else +#endif // XINERAMA + frame.changing = screen->availableArea(); + + switch(button) { + case 1: + blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert; + break; + + case 2: + blackbox_attrib.flags |= AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxVert; + + frame.changing.setX(frame.rect.x()); + frame.changing.setWidth(frame.rect.width()); + break; + + case 3: + blackbox_attrib.flags |= AttribMaxHoriz; + blackbox_attrib.attrib |= AttribMaxHoriz; + + frame.changing.setY(frame.rect.y()); + frame.changing.setHeight(frame.rect.height()); + break; + } + + constrain(TopLeft); + + if (flags.shaded) { + blackbox_attrib.flags ^= AttribShaded; + blackbox_attrib.attrib ^= AttribShaded; + flags.shaded = False; + } + + flags.maximized = button; + + configure(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); + if (flags.focused) + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + redrawAllButtons(); // in case it is not called in configure() + setState(current_state); +} + + +// re-maximizes the window to take into account availableArea changes +void BlackboxWindow::remaximize(void) { + if (flags.shaded) { + // we only update the window's attributes otherwise we lose the shade bit + switch(flags.maximized) { + case 1: + blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert; + break; + + case 2: + blackbox_attrib.flags |= AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxVert; + break; + + case 3: + blackbox_attrib.flags |= AttribMaxHoriz; + blackbox_attrib.attrib |= AttribMaxHoriz; + break; + } + return; + } + + // save the original dimensions because maximize will wipe them out + int premax_x = blackbox_attrib.premax_x, + premax_y = blackbox_attrib.premax_y, + premax_w = blackbox_attrib.premax_w, + premax_h = blackbox_attrib.premax_h; + + unsigned int button = flags.maximized; + flags.maximized = 0; // trick maximize() into working + maximize(button); + + // restore saved values + blackbox_attrib.premax_x = premax_x; + blackbox_attrib.premax_y = premax_y; + blackbox_attrib.premax_w = premax_w; + blackbox_attrib.premax_h = premax_h; +} + + +void BlackboxWindow::setWorkspace(unsigned int n) { + blackbox_attrib.flags |= AttribWorkspace; + blackbox_attrib.workspace = n; + if (n == BSENTINEL) { // iconified window + /* + we set the workspace to 'all workspaces' so that taskbars will show the + window. otherwise, it made uniconifying a window imposible without the + blackbox workspace menu + */ + n = 0xffffffff; + } + xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n); +} + + +void BlackboxWindow::shade(void) { + if (flags.shaded) { + XResizeWindow(otk::OBDisplay::display, frame.window, + frame.inside_w, frame.inside_h); + flags.shaded = False; + blackbox_attrib.flags ^= AttribShaded; + blackbox_attrib.attrib ^= AttribShaded; + + setState(NormalState); + + // set the frame rect to the normal size + frame.rect.setHeight(client.rect.height() + frame.margin.top + + frame.margin.bottom); + } else { + if (! (decorations & Decor_Titlebar)) + return; // can't shade it without a titlebar! + + XResizeWindow(otk::OBDisplay::display, frame.window, + frame.inside_w, frame.title_h); + flags.shaded = True; + blackbox_attrib.flags |= AttribShaded; + blackbox_attrib.attrib |= AttribShaded; + + setState(IconicState); + + // set the frame rect to the shaded size + frame.rect.setHeight(frame.title_h + (frame.border_w * 2)); + } +} + + +/* + * (Un)Sticks a window and its relatives. + */ +void BlackboxWindow::stick(void) { + if (flags.stuck) { + blackbox_attrib.flags ^= AttribOmnipresent; + blackbox_attrib.attrib ^= AttribOmnipresent; + + flags.stuck = False; + + for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) + if (i != blackbox_attrib.workspace) + screen->getWorkspace(i)->removeWindow(this, True); + + if (! flags.iconic) + screen->reassociateWindow(this, BSENTINEL, True); + // temporary fix since sticky windows suck. set the hint to what we + // actually hold in our data. + xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, + blackbox_attrib.workspace); + + setState(current_state); + } else { + flags.stuck = True; + + blackbox_attrib.flags |= AttribOmnipresent; + blackbox_attrib.attrib |= AttribOmnipresent; + + // temporary fix since sticky windows suck. set the hint to a different + // value than that contained in the class' data. + xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, + 0xffffffff); + + for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) + if (i != blackbox_attrib.workspace) + screen->getWorkspace(i)->addWindow(this, False, True); + + setState(current_state); + } + + redrawAllButtons(); + + // go up the chain + if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul && + client.transient_for->isStuck() != flags.stuck) + client.transient_for->stick(); + // go down the chain + BlackboxWindowList::iterator it; + const BlackboxWindowList::iterator end = client.transientList.end(); + for (it = client.transientList.begin(); it != end; ++it) + if ((*it)->isStuck() != flags.stuck) + (*it)->stick(); +} + + +void BlackboxWindow::redrawWindowFrame(void) const { + if (decorations & Decor_Titlebar) { + if (flags.focused) { + if (frame.ftitle) + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.title, frame.ftitle); + else + XSetWindowBackground(otk::OBDisplay::display, + frame.title, frame.ftitle_pixel); + } else { + if (frame.utitle) + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.title, frame.utitle); + else + XSetWindowBackground(otk::OBDisplay::display, + frame.title, frame.utitle_pixel); + } + XClearWindow(otk::OBDisplay::display, frame.title); + + redrawLabel(); + redrawAllButtons(); + } + + if (decorations & Decor_Handle) { + if (flags.focused) { + if (frame.fhandle) + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.handle, frame.fhandle); + else + XSetWindowBackground(otk::OBDisplay::display, + frame.handle, frame.fhandle_pixel); + + if (frame.fgrip) { + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.left_grip, frame.fgrip); + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.right_grip, frame.fgrip); + } else { + XSetWindowBackground(otk::OBDisplay::display, + frame.left_grip, frame.fgrip_pixel); + XSetWindowBackground(otk::OBDisplay::display, + frame.right_grip, frame.fgrip_pixel); + } + } else { + if (frame.uhandle) + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.handle, frame.uhandle); + else + XSetWindowBackground(otk::OBDisplay::display, + frame.handle, frame.uhandle_pixel); + + if (frame.ugrip) { + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.left_grip, frame.ugrip); + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.right_grip, frame.ugrip); + } else { + XSetWindowBackground(otk::OBDisplay::display, + frame.left_grip, frame.ugrip_pixel); + XSetWindowBackground(otk::OBDisplay::display, + frame.right_grip, frame.ugrip_pixel); + } + } + XClearWindow(otk::OBDisplay::display, frame.handle); + XClearWindow(otk::OBDisplay::display, frame.left_grip); + XClearWindow(otk::OBDisplay::display, frame.right_grip); + } + + if (decorations & Decor_Border) { + if (flags.focused) + XSetWindowBorder(otk::OBDisplay::display, + frame.plate, frame.fborder_pixel); + else + XSetWindowBorder(otk::OBDisplay::display, + frame.plate, frame.uborder_pixel); + } +} + + +void BlackboxWindow::setFocusFlag(bool focus) { + // only focus a window if it is visible + if (focus && ! flags.visible) + return; + + flags.focused = focus; + + redrawWindowFrame(); + + if (flags.focused) + blackbox->setFocusedWindow(this); +} + + +void BlackboxWindow::installColormap(bool install) { + int i = 0, ncmap = 0; + Colormap *cmaps = XListInstalledColormaps(otk::OBDisplay::display, + client.window, &ncmap); + if (cmaps) { + XWindowAttributes wattrib; + if (XGetWindowAttributes(otk::OBDisplay::display, + client.window, &wattrib)) { + if (install) { + // install the window's colormap + for (i = 0; i < ncmap; i++) { + if (*(cmaps + i) == wattrib.colormap) + // this window is using an installed color map... do not install + install = False; + } + // otherwise, install the window's colormap + if (install) + XInstallColormap(otk::OBDisplay::display, wattrib.colormap); + } else { + // uninstall the window's colormap + for (i = 0; i < ncmap; i++) { + if (*(cmaps + i) == wattrib.colormap) + // we found the colormap to uninstall + XUninstallColormap(otk::OBDisplay::display, wattrib.colormap); + } + } + } + + XFree(cmaps); + } +} + + +void BlackboxWindow::setAllowedActions(void) { + Atom actions[7]; + int num = 0; + + actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade); + actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop); + actions[num++] = xatom->getAtom(XAtom::net_wm_action_close); + + if (functions & Func_Move) + actions[num++] = xatom->getAtom(XAtom::net_wm_action_move); + if (functions & Func_Resize) + actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize); + if (functions & Func_Maximize) { + actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz); + actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert); + } + + xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom, + actions, num); +} + + +void BlackboxWindow::setState(unsigned long new_state) { + current_state = new_state; + + unsigned long state[2]; + state[0] = current_state; + state[1] = None; + xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2); + + xatom->setValue(client.window, XAtom::blackbox_attributes, + XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib, + PropBlackboxAttributesElements); + + Atom netstate[8]; + int num = 0; + if (flags.modal) + netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal); + if (flags.shaded) + netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded); + if (flags.iconic) + netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden); + if (flags.skip_taskbar) + netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar); + if (flags.skip_pager) + netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager); + if (flags.fullscreen) + netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen); + if (flags.maximized == 1 || flags.maximized == 2) + netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert); + if (flags.maximized == 1 || flags.maximized == 3) + netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz); + xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom, + netstate, num); +} + + +bool BlackboxWindow::getState(void) { + bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state, + current_state); + if (! ret) current_state = 0; + return ret; +} + + +void BlackboxWindow::restoreAttributes(void) { + unsigned long num = PropBlackboxAttributesElements; + BlackboxAttributes *net; + if (! xatom->getValue(client.window, XAtom::blackbox_attributes, + XAtom::blackbox_attributes, num, + (unsigned long **)&net)) + return; + if (num < PropBlackboxAttributesElements) { + delete [] net; + return; + } + + if (net->flags & AttribShaded && net->attrib & AttribShaded) { + flags.shaded = False; + unsigned long orig_state = current_state; + shade(); + + /* + At this point in the life of a window, current_state should only be set + to IconicState if the window was an *icon*, not if it was shaded. + */ + if (orig_state != IconicState) + current_state = WithdrawnState; + } + + if (net->workspace != screen->getCurrentWorkspaceID() && + net->workspace < screen->getWorkspaceCount()) + screen->reassociateWindow(this, net->workspace, True); + + if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) && + (blackbox_attrib.workspace < screen->getWorkspaceCount())) { + // set to WithdrawnState so it will be mapped on the new workspace + if (current_state == NormalState) current_state = WithdrawnState; + } else if (current_state == WithdrawnState) { + // the window is on this workspace and is Withdrawn, so it is waiting to + // be mapped + current_state = NormalState; + } + + if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent && + ! flags.stuck) { + stick(); + + // if the window was on another workspace, it was going to be hidden. this + // specifies that the window should be mapped since it is sticky. + if (current_state == WithdrawnState) current_state = NormalState; + } + + if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) { + int x = net->premax_x, y = net->premax_y; + unsigned int w = net->premax_w, h = net->premax_h; + flags.maximized = 0; + + unsigned int m = 0; + if ((net->flags & AttribMaxHoriz) && + (net->flags & AttribMaxVert)) + m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0; + else if (net->flags & AttribMaxVert) + m = (net->attrib & AttribMaxVert) ? 2 : 0; + else if (net->flags & AttribMaxHoriz) + m = (net->attrib & AttribMaxHoriz) ? 3 : 0; + + if (m) maximize(m); + + blackbox_attrib.premax_x = x; + blackbox_attrib.premax_y = y; + blackbox_attrib.premax_w = w; + blackbox_attrib.premax_h = h; + } + + if (net->flags & AttribDecoration) { + switch (net->decoration) { + case DecorNone: + enableDecor(False); + break; + + /* since tools only let you toggle this anyways, we'll just make that all + it supports for now. + */ + default: + case DecorNormal: + case DecorTiny: + case DecorTool: + enableDecor(True); + break; + } + } + + // with the state set it will then be the map event's job to read the + // window's state and behave accordingly + + delete [] net; +} + + +/* + * Positions the Rect r according the the client window position and + * window gravity. + */ +void BlackboxWindow::applyGravity(otk::Rect &r) { + // apply horizontal window gravity + switch (client.win_gravity) { + default: + case NorthWestGravity: + case SouthWestGravity: + case WestGravity: + r.setX(client.rect.x()); + break; + + case NorthGravity: + case SouthGravity: + case CenterGravity: + r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2); + break; + + case NorthEastGravity: + case SouthEastGravity: + case EastGravity: + r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2); + break; + + case ForgetGravity: + case StaticGravity: + r.setX(client.rect.x() - frame.margin.left); + break; + } + + // apply vertical window gravity + switch (client.win_gravity) { + default: + case NorthWestGravity: + case NorthEastGravity: + case NorthGravity: + r.setY(client.rect.y()); + break; + + case CenterGravity: + case EastGravity: + case WestGravity: + r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2); + break; + + case SouthWestGravity: + case SouthEastGravity: + case SouthGravity: + r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2); + break; + + case ForgetGravity: + case StaticGravity: + r.setY(client.rect.y() - frame.margin.top); + break; + } +} + + +/* + * The reverse of the applyGravity function. + * + * Positions the Rect r according to the frame window position and + * window gravity. + */ +void BlackboxWindow::restoreGravity(otk::Rect &r) { + // restore horizontal window gravity + switch (client.win_gravity) { + default: + case NorthWestGravity: + case SouthWestGravity: + case WestGravity: + r.setX(frame.rect.x()); + break; + + case NorthGravity: + case SouthGravity: + case CenterGravity: + r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2); + break; + + case NorthEastGravity: + case SouthEastGravity: + case EastGravity: + r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2); + break; + + case ForgetGravity: + case StaticGravity: + r.setX(frame.rect.x() + frame.margin.left); + break; + } + + // restore vertical window gravity + switch (client.win_gravity) { + default: + case NorthWestGravity: + case NorthEastGravity: + case NorthGravity: + r.setY(frame.rect.y()); + break; + + case CenterGravity: + case EastGravity: + case WestGravity: + r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2); + break; + + case SouthWestGravity: + case SouthEastGravity: + case SouthGravity: + r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2); + break; + + case ForgetGravity: + case StaticGravity: + r.setY(frame.rect.y() + frame.margin.top); + break; + } +} + + +void BlackboxWindow::redrawLabel(void) const { + if (flags.focused) { + if (frame.flabel) + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.label, frame.flabel); + else + XSetWindowBackground(otk::OBDisplay::display, + frame.label, frame.flabel_pixel); + } else { + if (frame.ulabel) + XSetWindowBackgroundPixmap(otk::OBDisplay::display, + frame.label, frame.ulabel); + else + XSetWindowBackground(otk::OBDisplay::display, + frame.label, frame.ulabel_pixel); + } + XClearWindow(otk::OBDisplay::display, frame.label); + + WindowStyle *style = screen->getWindowStyle(); + + int pos = frame.bevel_w * 2; + style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4); + style->font->drawString(frame.label, pos, 1, + (flags.focused ? style->l_text_focus : + style->l_text_unfocus), + client.title); +} + + +void BlackboxWindow::redrawAllButtons(void) const { + if (frame.iconify_button) redrawIconifyButton(False); + if (frame.maximize_button) redrawMaximizeButton(flags.maximized); + if (frame.close_button) redrawCloseButton(False); + if (frame.stick_button) redrawStickyButton(flags.stuck); +} + + +void BlackboxWindow::redrawButton(bool pressed, Window win, + Pixmap fppix, unsigned long fppixel, + Pixmap uppix, unsigned long uppixel, + Pixmap fpix, unsigned long fpixel, + Pixmap upix, unsigned long upixel) const { + Pixmap p; + unsigned long pix; + + if (pressed) { + if (flags.focused) { + p = fppix; + pix = fppixel; + } else { + p = uppix; + pix = uppixel; + } + } else { + if (flags.focused) { + p = fpix; + pix = fpixel; + } else { + p = upix; + pix = upixel; + } + } + + if (p) + XSetWindowBackgroundPixmap(otk::OBDisplay::display, win, p); + else + XSetWindowBackground(otk::OBDisplay::display, win, pix); + +} + +void BlackboxWindow::redrawIconifyButton(bool pressed) const { + redrawButton(pressed, frame.iconify_button, + frame.pfbutton, frame.pfbutton_pixel, + frame.pubutton, frame.pubutton_pixel, + frame.fbutton, frame.fbutton_pixel, + frame.ubutton, frame.ubutton_pixel); + + XClearWindow(otk::OBDisplay::display, frame.iconify_button); + otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : + screen->getWindowStyle()->b_pic_unfocus); + + PixmapMask pm = screen->getWindowStyle()->icon_button; + + if (screen->getWindowStyle()->icon_button.mask != None) { + XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask); + XSetClipOrigin(otk::OBDisplay::display, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); + + XFillRectangle(otk::OBDisplay::display, frame.iconify_button, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, + (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); + + XSetClipMask(otk::OBDisplay::display, pen.gc(), None); + XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0); + } else { + XDrawRectangle(otk::OBDisplay::display, frame.iconify_button, pen.gc(), + 2, (frame.button_w - 5), (frame.button_w - 5), 2); + } +} + + +void BlackboxWindow::redrawMaximizeButton(bool pressed) const { + redrawButton(pressed, frame.maximize_button, + frame.pfbutton, frame.pfbutton_pixel, + frame.pubutton, frame.pubutton_pixel, + frame.fbutton, frame.fbutton_pixel, + frame.ubutton, frame.ubutton_pixel); + + XClearWindow(otk::OBDisplay::display, frame.maximize_button); + + otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : + screen->getWindowStyle()->b_pic_unfocus); + + PixmapMask pm = screen->getWindowStyle()->max_button; + + if (pm.mask != None) { + XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask); + XSetClipOrigin(otk::OBDisplay::display, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); + + XFillRectangle(otk::OBDisplay::display, frame.maximize_button, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, + (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); + + XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 ); + XSetClipMask( otk::OBDisplay::display, pen.gc(), None ); + } else { + XDrawRectangle(otk::OBDisplay::display, frame.maximize_button, pen.gc(), + 2, 2, (frame.button_w - 5), (frame.button_w - 5)); + XDrawLine(otk::OBDisplay::display, frame.maximize_button, pen.gc(), + 2, 3, (frame.button_w - 3), 3); + } +} + + +void BlackboxWindow::redrawCloseButton(bool pressed) const { + redrawButton(pressed, frame.close_button, + frame.pfbutton, frame.pfbutton_pixel, + frame.pubutton, frame.pubutton_pixel, + frame.fbutton, frame.fbutton_pixel, + frame.ubutton, frame.ubutton_pixel); + + XClearWindow(otk::OBDisplay::display, frame.close_button); + + otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : + screen->getWindowStyle()->b_pic_unfocus); + + PixmapMask pm = screen->getWindowStyle()->close_button; + + if (pm.mask != None) { + XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask); + XSetClipOrigin(otk::OBDisplay::display, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); + + XFillRectangle(otk::OBDisplay::display, frame.close_button, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, + (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); + + + XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 ); + XSetClipMask( otk::OBDisplay::display, pen.gc(), None ); + } else { + XDrawLine(otk::OBDisplay::display, frame.close_button, pen.gc(), + 2, 2, (frame.button_w - 3), (frame.button_w - 3)); + XDrawLine(otk::OBDisplay::display, frame.close_button, pen.gc(), + 2, (frame.button_w - 3), (frame.button_w - 3), 2); + } +} + +void BlackboxWindow::redrawStickyButton(bool pressed) const { + redrawButton(pressed, frame.stick_button, + frame.pfbutton, frame.pfbutton_pixel, + frame.pubutton, frame.pubutton_pixel, + frame.fbutton, frame.fbutton_pixel, + frame.ubutton, frame.ubutton_pixel); + + XClearWindow(otk::OBDisplay::display, frame.stick_button); + + otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : + screen->getWindowStyle()->b_pic_unfocus); + + PixmapMask pm = screen->getWindowStyle()->stick_button; + + if (pm.mask != None) { + XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask); + XSetClipOrigin(otk::OBDisplay::display, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); + + XFillRectangle(otk::OBDisplay::display, frame.stick_button, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, + (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); + + + XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 ); + XSetClipMask( otk::OBDisplay::display, pen.gc(), None ); + } else { + XFillRectangle(otk::OBDisplay::display, frame.stick_button, pen.gc(), + frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 ); + } +} + +void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) { + if (re->window != client.window) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n", + client.window); +#endif // DEBUG + + /* + Even though the window wants to be shown, if it is not on the current + workspace, then it isn't going to be shown right now. + */ + if (! flags.stuck && + blackbox_attrib.workspace != screen->getCurrentWorkspaceID() && + blackbox_attrib.workspace < screen->getWorkspaceCount()) + if (current_state == NormalState) current_state = WithdrawnState; + + switch (current_state) { + case IconicState: + iconify(); + break; + + case WithdrawnState: + withdraw(); + break; + + case NormalState: + case InactiveState: + case ZoomState: + default: + show(); + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + if (isNormal()) { + if (blackbox->state() != Openbox::State_Starting) { + XSync(otk::OBDisplay::display, False); // make sure the frame is mapped + if (screen->doFocusNew() || (isTransient() && getTransientFor() && + getTransientFor()->isFocused())) { + setInputFocus(); + } + if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) { + int x, y, rx, ry; + Window c, r; + unsigned int m; + XQueryPointer(otk::OBDisplay::display, screen->getRootWindow(), + &r, &c, &rx, &ry, &x, &y, &m); + beginMove(rx, ry); + } + } + } + break; + } +} + + +void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) { + if (ue->window != client.window) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n", + client.window); +#endif // DEBUG + + screen->unmanageWindow(this, False); +} + + +void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) { + if (de->window != client.window) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n", + client.window); +#endif // DEBUG + + screen->unmanageWindow(this, False); +} + + +void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) { + if (re->window != client.window || re->parent == frame.plate) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to " + "0x%lx.\n", client.window, re->parent); +#endif // DEBUG + + XEvent ev; + ev.xreparent = *re; + XPutBackEvent(otk::OBDisplay::display, &ev); + screen->unmanageWindow(this, True); +} + + +void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) { + if (pe->state == PropertyDelete || ! validateClient()) + return; + +#if 0 + fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n", + client.window); +#endif + + switch(pe->atom) { + case XA_WM_CLASS: + case XA_WM_CLIENT_MACHINE: + case XA_WM_COMMAND: + break; + + case XA_WM_TRANSIENT_FOR: { + bool s = flags.stuck; + + // determine if this is a transient window + getTransientInfo(); + + if (flags.stuck != s) stick(); + + // adjust the window decorations based on transience + if (isTransient()) { + functions &= ~Func_Maximize; + setAllowedActions(); + setupDecor(); + } + + reconfigure(); + } + break; + + case XA_WM_HINTS: + getWMHints(); + break; + + case XA_WM_ICON_NAME: + getWMIconName(); + if (flags.iconic) screen->propagateWindowName(this); + break; + + case XAtom::net_wm_name: + case XA_WM_NAME: + getWMName(); + + if (decorations & Decor_Titlebar) + redrawLabel(); + + screen->propagateWindowName(this); + break; + + case XA_WM_NORMAL_HINTS: { + getWMNormalHints(); + + if ((client.normal_hint_flags & PMinSize) && + (client.normal_hint_flags & PMaxSize)) { + // the window now can/can't resize itself, so the buttons need to be + // regrabbed. + ungrabButtons(); + if (client.max_width <= client.min_width && + client.max_height <= client.min_height) { + functions &= ~(Func_Resize | Func_Maximize); + } else { + if (! isTransient()) + functions |= Func_Maximize; + functions |= Func_Resize; + } + grabButtons(); + setAllowedActions(); + setupDecor(); + } + + otk::Rect old_rect = frame.rect; + + upsize(); + + if (old_rect != frame.rect) + reconfigure(); + + break; + } + + default: + if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) { + getWMProtocols(); + + if ((decorations & Decor_Close) && (! frame.close_button)) { + createCloseButton(); + if (decorations & Decor_Titlebar) { + positionButtons(True); + XMapSubwindows(otk::OBDisplay::display, frame.title); + } + } + } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) { + updateStrut(); + } + + break; + } +} + + +void BlackboxWindow::exposeEvent(const XExposeEvent *ee) { +#if 0 + fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window); +#endif + + if (frame.label == ee->window && (decorations & Decor_Titlebar)) + redrawLabel(); + else if (frame.close_button == ee->window) + redrawCloseButton(False); + else if (frame.maximize_button == ee->window) + redrawMaximizeButton(flags.maximized); + else if (frame.iconify_button == ee->window) + redrawIconifyButton(False); + else if (frame.stick_button == ee->window) + redrawStickyButton(flags.stuck); +} + + +void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) { + if (cr->window != client.window || flags.iconic) + return; + + if (cr->value_mask & CWBorderWidth) + client.old_bw = cr->border_width; + + if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) { + frame.changing = frame.rect; + + if (cr->value_mask & (CWX | CWY)) { + if (cr->value_mask & CWX) + client.rect.setX(cr->x); + if (cr->value_mask & CWY) + client.rect.setY(cr->y); + + applyGravity(frame.changing); + } + + if (cr->value_mask & (CWWidth | CWHeight)) { + if (cr->value_mask & CWWidth) + frame.changing.setWidth(cr->width + + frame.margin.left + frame.margin.right); + + if (cr->value_mask & CWHeight) + frame.changing.setHeight(cr->height + + frame.margin.top + frame.margin.bottom); + + /* + if a position change has been specified, then that position will be + used instead of determining a position based on the window's gravity. + */ + if (! (cr->value_mask & (CWX | CWY))) { + Corner corner; + switch (client.win_gravity) { + case NorthEastGravity: + case EastGravity: + corner = TopRight; + break; + case SouthWestGravity: + case SouthGravity: + corner = BottomLeft; + break; + case SouthEastGravity: + corner = BottomRight; + break; + default: // NorthWest, Static, etc + corner = TopLeft; + } + constrain(corner); + } + } + + configure(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); + } + + if (cr->value_mask & CWStackMode && !isDesktop()) { + switch (cr->detail) { + case Below: + case BottomIf: + screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); + break; + + case Above: + case TopIf: + default: + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + break; + } + } +} + + +void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) { +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n", + client.window); +#endif + + if (frame.maximize_button == be->window && be->button <= 3) { + redrawMaximizeButton(True); + } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) { + if (! flags.focused) + setInputFocus(); + + if (frame.iconify_button == be->window) { + redrawIconifyButton(True); + } else if (frame.close_button == be->window) { + redrawCloseButton(True); + } else if (frame.stick_button == be->window) { + redrawStickyButton(True); + } else if (frame.plate == be->window) { + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + + XAllowEvents(otk::OBDisplay::display, ReplayPointer, be->time); + } else { + if (frame.title == be->window || frame.label == be->window) { + if (((be->time - lastButtonPressTime) <= + blackbox->getDoubleClickInterval()) || + (be->state == ControlMask)) { + lastButtonPressTime = 0; + shade(); + } else { + lastButtonPressTime = be->time; + } + } + + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + } + } else if (be->button == 2 && (be->window != frame.iconify_button) && + (be->window != frame.close_button) && + (be->window != frame.stick_button)) { + screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); + // mouse wheel up + } else if (be->button == 4) { + if ((be->window == frame.label || + be->window == frame.title || + be->window == frame.maximize_button || + be->window == frame.iconify_button || + be->window == frame.close_button || + be->window == frame.stick_button) && + ! flags.shaded) + shade(); + // mouse wheel down + } else if (be->button == 5) { + if ((be->window == frame.label || + be->window == frame.title || + be->window == frame.maximize_button || + be->window == frame.iconify_button || + be->window == frame.close_button || + be->window == frame.stick_button) && + flags.shaded) + shade(); + } +} + + +void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) { +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n", + client.window); +#endif + + if (re->window == frame.maximize_button && + re->button >= 1 && re->button <= 3) { + if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && + (re->y >= 0 && re->y <= static_cast(frame.button_w))) { + maximize(re->button); + } else { + redrawMaximizeButton(flags.maximized); + } + } else if (re->window == frame.iconify_button && re->button == 1) { + if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && + (re->y >= 0 && re->y <= static_cast(frame.button_w))) { + iconify(); + } else { + redrawIconifyButton(False); + } + } else if (re->window == frame.stick_button && re->button == 1) { + if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && + (re->y >= 0 && re->y <= static_cast(frame.button_w))) { + stick(); + } else { + redrawStickyButton(False); + } + } else if (re->window == frame.close_button & re->button == 1) { + if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && + (re->y >= 0 && re->y <= static_cast(frame.button_w))) + close(); + redrawCloseButton(False); + } else if (flags.moving) { + endMove(); + } else if (flags.resizing) { + endResize(); + } else if (re->window == frame.window) { + if (re->button == 2 && re->state == mod_mask) + XUngrabPointer(otk::OBDisplay::display, CurrentTime); + } +} + + + +void BlackboxWindow::beginMove(int x_root, int y_root) { + if (! (functions & Func_Move)) return; + + assert(! (flags.resizing || flags.moving)); + + /* + Only one window can be moved/resized at a time. If another window is already + being moved or resized, then stop it before whating to work with this one. + */ + BlackboxWindow *changing = blackbox->getChangingWindow(); + if (changing && changing != this) { + if (changing->flags.moving) + changing->endMove(); + else // if (changing->flags.resizing) + changing->endResize(); + } + + XGrabPointer(otk::OBDisplay::display, frame.window, False, + PointerMotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, + None, blackbox->getMoveCursor(), CurrentTime); + + flags.moving = True; + blackbox->setChangingWindow(this); + + if (! screen->doOpaqueMove()) { + XGrabServer(otk::OBDisplay::display); + + frame.changing = frame.rect; + screen->showPosition(frame.changing.x(), frame.changing.y()); + + XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), + screen->getOpGC(), + frame.changing.x(), + frame.changing.y(), + frame.changing.width() - 1, + frame.changing.height() - 1); + } + + frame.grab_x = x_root - frame.rect.x() - frame.border_w; + frame.grab_y = y_root - frame.rect.y() - frame.border_w; +} + + +void BlackboxWindow::doMove(int x_root, int y_root) { + assert(flags.moving); + assert(blackbox->getChangingWindow() == this); + + int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y; + dx -= frame.border_w; + dy -= frame.border_w; + + doWindowSnapping(dx, dy); + + if (screen->doOpaqueMove()) { + if (screen->doWorkspaceWarping()) + doWorkspaceWarping(x_root, y_root, dx); + + configure(dx, dy, frame.rect.width(), frame.rect.height()); + } else { + XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), + screen->getOpGC(), + frame.changing.x(), + frame.changing.y(), + frame.changing.width() - 1, + frame.changing.height() - 1); + + if (screen->doWorkspaceWarping()) + doWorkspaceWarping(x_root, y_root, dx); + + frame.changing.setPos(dx, dy); + + XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), + screen->getOpGC(), + frame.changing.x(), + frame.changing.y(), + frame.changing.width() - 1, + frame.changing.height() - 1); + } + + screen->showPosition(dx, dy); +} + + +void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) { + // workspace warping + bool warp = False; + unsigned int dest = screen->getCurrentWorkspaceID(); + if (x_root <= 0) { + warp = True; + + if (dest > 0) dest--; + else dest = screen->getNumberOfWorkspaces() - 1; + + } else if (x_root >= screen->getRect().right()) { + warp = True; + + if (dest < screen->getNumberOfWorkspaces() - 1) dest++; + else dest = 0; + } + if (! warp) + return; + + bool focus = flags.focused; // had focus while moving? + + int dest_x = x_root; + if (x_root <= 0) { + dest_x += screen->getRect().width() - 1; + dx += screen->getRect().width() - 1; + } else { + dest_x -= screen->getRect().width() - 1; + dx -= screen->getRect().width() - 1; + } + + if (! flags.stuck) + screen->reassociateWindow(this, dest, False); + screen->changeWorkspaceID(dest); + + if (screen->doOpaqueMove()) + XGrabServer(otk::OBDisplay::display); + + XUngrabPointer(otk::OBDisplay::display, CurrentTime); + XWarpPointer(otk::OBDisplay::display, None, + screen->getRootWindow(), 0, 0, 0, 0, + dest_x, y_root); + XGrabPointer(otk::OBDisplay::display, frame.window, False, + PointerMotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, + None, blackbox->getMoveCursor(), CurrentTime); + + if (screen->doOpaqueMove()) + XUngrabServer(otk::OBDisplay::display); + + if (focus) + setInputFocus(); + +} + + +void BlackboxWindow::doWindowSnapping(int &dx, int &dy) { + // how much resistance to edges to provide + const int resistance_size = screen->getResistanceSize(); + + // how far away to snap + const int snap_distance = screen->getSnapThreshold(); + + // how to snap windows + const int snap_to_windows = screen->getWindowToWindowSnap(); + const int snap_to_edges = screen->getWindowToEdgeSnap(); + // the amount of space away from the edge to provide resistance/snap + const int snap_offset = screen->getSnapOffset(); + + // find the geomeetery where the moving window currently is + const otk::Rect &moving = + screen->doOpaqueMove() ? frame.rect : frame.changing; + + // window corners + const int wleft = dx, + wright = dx + frame.rect.width() - 1, + wtop = dy, + wbottom = dy + frame.rect.height() - 1; + + if (snap_to_windows) { + otk::RectList rectlist; + + Workspace *w = screen->getWorkspace(getWorkspaceNumber()); + assert(w); + + // add windows on the workspace to the rect list + const BlackboxWindowList& stack_list = w->getStackingList(); + BlackboxWindowList::const_iterator st_it, st_end = stack_list.end(); + for (st_it = stack_list.begin(); st_it != st_end; ++st_it) + if (*st_it != this) // don't snap to ourself + rectlist.push_back( (*st_it)->frameRect() ); + + otk::RectList::const_iterator it, end = rectlist.end(); + for (it = rectlist.begin(); it != end; ++it) { + bool snapped = False; + const otk::Rect &winrect = *it; + otk::Rect offsetrect; + offsetrect.setCoords(winrect.left() - snap_offset, + winrect.top() - snap_offset, + winrect.right() + snap_offset, + winrect.bottom() + snap_offset); + + if (snap_to_windows == BScreen::WindowResistance) + // if the window is already over top of this snap target, then + // resistance is futile, so just ignore it + if (winrect.intersects(moving)) + continue; + + int dleft, dright, dtop, dbottom; + + // if the windows are in the same plane vertically + if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) && + wtop < (signed)(winrect.y() + winrect.height() - 1)) { + + if (snap_to_windows == BScreen::WindowResistance) { + dleft = wright - offsetrect.left(); + dright = offsetrect.right() - wleft; + + // snap left of other window? + if (dleft >= 0 && dleft < resistance_size && + dleft < (wright - wleft)) { + dx = offsetrect.left() - frame.rect.width(); + snapped = True; + } + // snap right of other window? + else if (dright >= 0 && dright < resistance_size && + dright < (wright - wleft)) { + dx = offsetrect.right() + 1; + snapped = True; + } + } else { // BScreen::WindowSnap + dleft = abs(wright - offsetrect.left()); + dright = abs(wleft - offsetrect.right()); + + // snap left of other window? + if (dleft < snap_distance && dleft <= dright) { + dx = offsetrect.left() - frame.rect.width(); + snapped = True; + } + // snap right of other window? + else if (dright < snap_distance) { + dx = offsetrect.right() + 1; + snapped = True; + } + } + + if (snapped) { + if (screen->getWindowCornerSnap()) { + // try corner-snap to its other sides + if (snap_to_windows == BScreen::WindowResistance) { + dtop = winrect.top() - wtop; + dbottom = wbottom - winrect.bottom(); + if (dtop > 0 && dtop < resistance_size) { + // if we're already past the top edge, then don't provide + // resistance + if (moving.top() >= winrect.top()) + dy = winrect.top(); + } else if (dbottom > 0 && dbottom < resistance_size) { + // if we're already past the bottom edge, then don't provide + // resistance + if (moving.bottom() <= winrect.bottom()) + dy = winrect.bottom() - frame.rect.height() + 1; + } + } else { // BScreen::WindowSnap + dtop = abs(wtop - winrect.top()); + dbottom = abs(wbottom - winrect.bottom()); + if (dtop < snap_distance && dtop <= dbottom) + dy = winrect.top(); + else if (dbottom < snap_distance) + dy = winrect.bottom() - frame.rect.height() + 1; + } + } + + continue; + } + } + + // if the windows are on the same plane horizontally + if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) && + wleft < (signed)(winrect.x() + winrect.width() - 1)) { + + if (snap_to_windows == BScreen::WindowResistance) { + dtop = wbottom - offsetrect.top(); + dbottom = offsetrect.bottom() - wtop; + + // snap top of other window? + if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) { + dy = offsetrect.top() - frame.rect.height(); + snapped = True; + } + // snap bottom of other window? + else if (dbottom >= 0 && dbottom < resistance_size && + dbottom < (wbottom - wtop)) { + dy = offsetrect.bottom() + 1; + snapped = True; + } + } else { // BScreen::WindowSnap + dtop = abs(wbottom - offsetrect.top()); + dbottom = abs(wtop - offsetrect.bottom()); + + // snap top of other window? + if (dtop < snap_distance && dtop <= dbottom) { + dy = offsetrect.top() - frame.rect.height(); + snapped = True; + } + // snap bottom of other window? + else if (dbottom < snap_distance) { + dy = offsetrect.bottom() + 1; + snapped = True; + } + + } + + if (snapped) { + if (screen->getWindowCornerSnap()) { + // try corner-snap to its other sides + if (snap_to_windows == BScreen::WindowResistance) { + dleft = winrect.left() - wleft; + dright = wright - winrect.right(); + if (dleft > 0 && dleft < resistance_size) { + // if we're already past the left edge, then don't provide + // resistance + if (moving.left() >= winrect.left()) + dx = winrect.left(); + } else if (dright > 0 && dright < resistance_size) { + // if we're already past the right edge, then don't provide + // resistance + if (moving.right() <= winrect.right()) + dx = winrect.right() - frame.rect.width() + 1; + } + } else { // BScreen::WindowSnap + dleft = abs(wleft - winrect.left()); + dright = abs(wright - winrect.right()); + if (dleft < snap_distance && dleft <= dright) + dx = winrect.left(); + else if (dright < snap_distance) + dx = winrect.right() - frame.rect.width() + 1; + } + } + + continue; + } + } + } + } + + if (snap_to_edges) { + otk::RectList rectlist; + + // snap to the screen edges (and screen boundaries for xinerama) +#ifdef XINERAMA + if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) { + rectlist.insert(rectlist.begin(), + screen->getXineramaAreas().begin(), + screen->getXineramaAreas().end()); + } else +#endif // XINERAMA + rectlist.push_back(screen->getRect()); + + otk::RectList::const_iterator it, end = rectlist.end(); + for (it = rectlist.begin(); it != end; ++it) { + const otk::Rect &srect = *it; + otk::Rect offsetrect; + offsetrect.setCoords(srect.left() + snap_offset, + srect.top() + snap_offset, + srect.right() - snap_offset, + srect.bottom() - snap_offset); + + if (snap_to_edges == BScreen::WindowResistance) { + // if we're not in the rectangle then don't snap to it. + if (! srect.contains(moving)) + continue; + } else { // BScreen::WindowSnap + // if we're not in the rectangle then don't snap to it. + if (! srect.intersects(otk::Rect(wleft, wtop, frame.rect.width(), + frame.rect.height()))) + continue; + } + + if (snap_to_edges == BScreen::WindowResistance) { + int dleft = offsetrect.left() - wleft, + dright = wright - offsetrect.right(), + dtop = offsetrect.top() - wtop, + dbottom = wbottom - offsetrect.bottom(); + + // snap left? + if (dleft > 0 && dleft < resistance_size) + dx = offsetrect.left(); + // snap right? + else if (dright > 0 && dright < resistance_size) + dx = offsetrect.right() - frame.rect.width() + 1; + + // snap top? + if (dtop > 0 && dtop < resistance_size) + dy = offsetrect.top(); + // snap bottom? + else if (dbottom > 0 && dbottom < resistance_size) + dy = offsetrect.bottom() - frame.rect.height() + 1; + } else { // BScreen::WindowSnap + int dleft = abs(wleft - offsetrect.left()), + dright = abs(wright - offsetrect.right()), + dtop = abs(wtop - offsetrect.top()), + dbottom = abs(wbottom - offsetrect.bottom()); + + // snap left? + if (dleft < snap_distance && dleft <= dright) + dx = offsetrect.left(); + // snap right? + else if (dright < snap_distance) + dx = offsetrect.right() - frame.rect.width() + 1; + + // snap top? + if (dtop < snap_distance && dtop <= dbottom) + dy = offsetrect.top(); + // snap bottom? + else if (dbottom < snap_distance) + dy = offsetrect.bottom() - frame.rect.height() + 1; + } + } + } +} + + +void BlackboxWindow::endMove(void) { + assert(flags.moving); + assert(blackbox->getChangingWindow() == this); + + flags.moving = False; + blackbox->setChangingWindow(0); + + if (! screen->doOpaqueMove()) { + /* when drawing the rubber band, we need to make sure we only draw inside + * the frame... frame.changing_* contain the new coords for the window, + * so we need to subtract 1 from changing_w/changing_h every where we + * draw the rubber band (for both moving and resizing) + */ + XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + XUngrabServer(otk::OBDisplay::display); + + configure(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); + } else { + configure(frame.rect.x(), frame.rect.y(), + frame.rect.width(), frame.rect.height()); + } + screen->hideGeometry(); + + XUngrabPointer(otk::OBDisplay::display, CurrentTime); + + // if there are any left over motions from the move, drop them now + XSync(otk::OBDisplay::display, false); // make sure we don't miss any + XEvent e; + while (XCheckTypedWindowEvent(otk::OBDisplay::display, frame.window, + MotionNotify, &e)); +} + + +void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) { + if (! (functions & Func_Resize)) return; + + assert(! (flags.resizing || flags.moving)); + + /* + Only one window can be moved/resized at a time. If another window is + already being moved or resized, then stop it before whating to work with + this one. + */ + BlackboxWindow *changing = blackbox->getChangingWindow(); + if (changing && changing != this) { + if (changing->flags.moving) + changing->endMove(); + else // if (changing->flags.resizing) + changing->endResize(); + } + + resize_dir = dir; + + Cursor cursor; + Corner anchor; + + switch (resize_dir) { + case BottomLeft: + anchor = TopRight; + cursor = blackbox->getLowerLeftAngleCursor(); + break; + + case BottomRight: + anchor = TopLeft; + cursor = blackbox->getLowerRightAngleCursor(); + break; + + case TopLeft: + anchor = BottomRight; + cursor = blackbox->getUpperLeftAngleCursor(); + break; + + case TopRight: + anchor = BottomLeft; + cursor = blackbox->getUpperRightAngleCursor(); + break; + + default: + assert(false); // unhandled Corner + return; // unreachable, for the compiler + } + + XGrabServer(otk::OBDisplay::display); + XGrabPointer(otk::OBDisplay::display, frame.window, False, + PointerMotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime); + + flags.resizing = True; + blackbox->setChangingWindow(this); + + unsigned int gw, gh; + frame.changing = frame.rect; + + constrain(anchor, &gw, &gh); + + XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + + screen->showGeometry(gw, gh); + + frame.grab_x = x_root; + frame.grab_y = y_root; +} + + +void BlackboxWindow::doResize(int x_root, int y_root) { + assert(flags.resizing); + assert(blackbox->getChangingWindow() == this); + + XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + + unsigned int gw, gh; + Corner anchor; + int dx, dy; // the amount of change in the size of the window + + switch (resize_dir) { + case BottomLeft: + anchor = TopRight; + dx = - (x_root - frame.grab_x); + dy = + (y_root - frame.grab_y); + break; + case BottomRight: + anchor = TopLeft; + dx = + (x_root - frame.grab_x); + dy = + (y_root - frame.grab_y); + break; + case TopLeft: + anchor = BottomRight; + dx = - (x_root - frame.grab_x); + dy = - (y_root - frame.grab_y); + break; + case TopRight: + anchor = BottomLeft; + dx = + (x_root - frame.grab_x); + dy = - (y_root - frame.grab_y); + break; + + default: + assert(false); // unhandled Corner + return; // unreachable, for the compiler + } + + // make sure the user cant resize the window smaller than 0, which makes it + // wrap around and become huge + if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width(); + if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height(); + + frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy); + + constrain(anchor, &gw, &gh); + + XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + + screen->showGeometry(gw, gh); +} + + +void BlackboxWindow::endResize(void) { + assert(flags.resizing); + assert(blackbox->getChangingWindow() == this); + + XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + XUngrabServer(otk::OBDisplay::display); + + // unset maximized state after resized when fully maximized + if (flags.maximized == 1) + maximize(0); + + flags.resizing = False; + blackbox->setChangingWindow(0); + + configure(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); + screen->hideGeometry(); + + XUngrabPointer(otk::OBDisplay::display, CurrentTime); + + // if there are any left over motions from the resize, drop them now + XSync(otk::OBDisplay::display, false); // make sure we don't miss any + XEvent e; + while (XCheckTypedWindowEvent(otk::OBDisplay::display, frame.window, + MotionNotify, &e)); +} + + +void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) { +#if 0 + fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n", + client.window); +#endif + + if (flags.moving) { + doMove(me->x_root, me->y_root); + } else if (flags.resizing) { + doResize(me->x_root, me->y_root); + } else { + if ((functions & Func_Move) && + (me->state & Button1Mask) && + (frame.title == me->window || frame.label == me->window || + frame.handle == me->window || frame.window == me->window)) { + beginMove(me->x_root, me->y_root); + } else if ((functions & Func_Resize) && + ((me->state & Button1Mask) && + (me->window == frame.right_grip || + me->window == frame.left_grip)) || + ((me->state & Button3Mask) && (me->state & mod_mask) && + (frame.title == me->window || frame.label == me->window || + frame.handle == me->window || frame.window == me->window || + frame.right_grip == me->window || + frame.left_grip == me->window))) { + unsigned int zones = screen->getResizeZones(); + Corner corner; + + if (me->window == frame.left_grip) { + corner = BottomLeft; + } else if (me->window == frame.right_grip || zones == 1) { + corner = BottomRight; + } else { + bool top; + bool left = (me->x_root - frame.rect.x() <= + static_cast(frame.rect.width() / 2)); + if (zones == 2) + top = False; + else // (zones == 4) + top = (me->y_root - frame.rect.y() <= + static_cast(frame.rect.height() / 2)); + corner = (top ? (left ? TopLeft : TopRight) : + (left ? BottomLeft : BottomRight)); + } + + beginResize(me->x_root, me->y_root, corner); + } + } +} + + +void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) { + if (! (screen->isSloppyFocus() && isVisible() && isNormal())) + return; + + XEvent e; + bool leave = False, inferior = False; + + while (XCheckTypedWindowEvent(otk::OBDisplay::display, ce->window, + LeaveNotify, &e)) { + if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) { + leave = True; + inferior = (e.xcrossing.detail == NotifyInferior); + } + } + + if (! leave || inferior) { + if (! isFocused()) { + bool success = setInputFocus(); + if (success) // if focus succeeded install the colormap + installColormap(True); // XXX: shouldnt we honour no install? + + /* + We only auto-raise when the window wasn't focused because otherwise + we run into problems with gtk+ drop-down lists. The window ends up + raising over the list. + */ + if (screen->doAutoRaise()) + timer->start(); + } + } +} + + +void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) { + if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal())) + return; + + installColormap(False); + + timer->stop(); +} + + +#ifdef SHAPE +void BlackboxWindow::shapeEvent(XShapeEvent *e) { + if (blackbox->hasShapeExtensions()) { + if (! e->shaped && flags.shaped) { + clearShape(); + flags.shaped = False; + } else if (e->shaped) { + configureShape(); + flags.shaped = True; + } + } +} +#endif // SHAPE + + +bool BlackboxWindow::validateClient(void) const { + XSync(otk::OBDisplay::display, False); + + XEvent e; + if (XCheckTypedWindowEvent(otk::OBDisplay::display, client.window, + DestroyNotify, &e) || + XCheckTypedWindowEvent(otk::OBDisplay::display, client.window, + UnmapNotify, &e)) { + XPutBackEvent(otk::OBDisplay::display, &e); + + return False; + } + + return True; +} + + +void BlackboxWindow::restore(bool remap) { + XChangeSaveSet(otk::OBDisplay::display, client.window, SetModeDelete); + XSelectInput(otk::OBDisplay::display, client.window, NoEventMask); + XSelectInput(otk::OBDisplay::display, frame.plate, NoEventMask); + + // do not leave a shaded window as an icon unless it was an icon + if (flags.shaded && ! flags.iconic) + setState(NormalState); + + // erase the netwm stuff that we read when a window maps, so that it + // doesn't persist between mappings. + // (these are the ones read in getNetWMFlags().) + xatom->eraseValue(client.window, XAtom::net_wm_desktop); + xatom->eraseValue(client.window, XAtom::net_wm_state); + + restoreGravity(client.rect); + + XUnmapWindow(otk::OBDisplay::display, frame.window); + XUnmapWindow(otk::OBDisplay::display, client.window); + + XSetWindowBorderWidth(otk::OBDisplay::display, client.window, client.old_bw); + + XEvent ev; + if (XCheckTypedWindowEvent(otk::OBDisplay::display, client.window, + ReparentNotify, &ev)) { + remap = True; + } else { + // according to the ICCCM - if the client doesn't reparent to + // root, then we have to do it for them + XReparentWindow(otk::OBDisplay::display, client.window, + screen->getRootWindow(), + client.rect.x(), client.rect.y()); + } + + if (remap) XMapWindow(otk::OBDisplay::display, client.window); +} + + +// timer for autoraise +void BlackboxWindow::timeout(BlackboxWindow *t) { + t->screen->getWorkspace(t->blackbox_attrib.workspace)->raiseWindow(t); + printf("TIMED OUT YA YAY\n"); +} + + +void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) { + if ((net->flags & AttribShaded) && + ((blackbox_attrib.attrib & AttribShaded) != + (net->attrib & AttribShaded))) + shade(); + + if (flags.visible && // watch out for requests when we can not be seen + (net->flags & (AttribMaxVert | AttribMaxHoriz)) && + ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) != + (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) { + if (flags.maximized) { + maximize(0); + } else { + int button = 0; + + if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert)) + button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0); + else if (net->flags & AttribMaxVert) + button = ((net->attrib & AttribMaxVert) ? 2 : 0); + else if (net->flags & AttribMaxHoriz) + button = ((net->attrib & AttribMaxHoriz) ? 3 : 0); + + maximize(button); + } + } + + if ((net->flags & AttribOmnipresent) && + ((blackbox_attrib.attrib & AttribOmnipresent) != + (net->attrib & AttribOmnipresent))) + stick(); + + if ((net->flags & AttribWorkspace) && + (blackbox_attrib.workspace != net->workspace)) { + screen->reassociateWindow(this, net->workspace, True); + + if (screen->getCurrentWorkspaceID() != net->workspace) { + withdraw(); + } else { + show(); + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + } + } + + if (net->flags & AttribDecoration) { + switch (net->decoration) { + case DecorNone: + enableDecor(False); + break; + + default: + case DecorNormal: + case DecorTiny: + case DecorTool: + enableDecor(True); + break; + } + } +} + + +/* + * Set the sizes of all components of the window frame + * (the window decorations). + * These values are based upon the current style settings and the client + * window's dimensions. + */ +void BlackboxWindow::upsize(void) { + frame.bevel_w = screen->getBevelWidth(); + + if (decorations & Decor_Border) { + frame.border_w = screen->getBorderWidth(); + if (! isTransient()) + frame.mwm_border_w = screen->getFrameWidth(); + else + frame.mwm_border_w = 0; + } else { + frame.mwm_border_w = frame.border_w = 0; + } + + if (decorations & Decor_Titlebar) { + // the height of the titlebar is based upon the height of the font being + // used to display the window's title + WindowStyle *style = screen->getWindowStyle(); + frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2; + + frame.label_h = frame.title_h - (frame.bevel_w * 2); + frame.button_w = (frame.label_h - 2); + + // set the top frame margin + frame.margin.top = frame.border_w + frame.title_h + + frame.border_w + frame.mwm_border_w; + } else { + frame.title_h = 0; + frame.label_h = 0; + frame.button_w = 0; + + // set the top frame margin + frame.margin.top = frame.border_w + frame.mwm_border_w; + } + + // set the left/right frame margin + frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w; + + if (decorations & Decor_Handle) { + frame.grip_w = frame.button_w * 2; + frame.handle_h = screen->getHandleWidth(); + + // set the bottom frame margin + frame.margin.bottom = frame.border_w + frame.handle_h + + frame.border_w + frame.mwm_border_w; + } else { + frame.handle_h = 0; + frame.grip_w = 0; + + // set the bottom frame margin + frame.margin.bottom = frame.border_w + frame.mwm_border_w; + } + + /* + We first get the normal dimensions and use this to define the inside_w/h + then we modify the height if shading is in effect. + If the shade state is not considered then frame.rect gets reset to the + normal window size on a reconfigure() call resulting in improper + dimensions appearing in move/resize and other events. + */ + unsigned int + height = client.rect.height() + frame.margin.top + frame.margin.bottom, + width = client.rect.width() + frame.margin.left + frame.margin.right; + + frame.inside_w = width - (frame.border_w * 2); + frame.inside_h = height - (frame.border_w * 2); + + if (flags.shaded) + height = frame.title_h + (frame.border_w * 2); + frame.rect.setSize(width, height); +} + + +/* + * Calculate the size of the client window and constrain it to the + * size specified by the size hints of the client window. + * + * The logical width and height are placed into pw and ph, if they + * are non-zero. Logical size refers to the users perception of + * the window size (for example an xterm resizes in cells, not in pixels). + * pw and ph are then used to display the geometry during window moves, resize, + * etc. + * + * The physical geometry is placed into frame.changing_{x,y,width,height}. + * Physical geometry refers to the geometry of the window in pixels. + */ +void BlackboxWindow::constrain(Corner anchor, + unsigned int *pw, unsigned int *ph) { + // frame.changing represents the requested frame size, we need to + // strip the frame margin off and constrain the client size + frame.changing.setCoords(frame.changing.left() + frame.margin.left, + frame.changing.top() + frame.margin.top, + frame.changing.right() - frame.margin.right, + frame.changing.bottom() - frame.margin.bottom); + + unsigned int dw = frame.changing.width(), dh = frame.changing.height(), + base_width = (client.base_width) ? client.base_width : client.min_width, + base_height = (client.base_height) ? client.base_height : + client.min_height; + + // constrain, but only if the min/max are being used. if they aren't, then + // this resize is going to be from a ConfigureRequest because the window + // isn't allowed to be resized by the user. And in that case, we don't want + // to limit what the app can do + if (client.max_width > client.min_width || + client.max_height > client.min_height) { + if (dw < client.min_width) dw = client.min_width; + if (dh < client.min_height) dh = client.min_height; + if (dw > client.max_width) dw = client.max_width; + if (dh > client.max_height) dh = client.max_height; + } + + if (client.width_inc > 1) { + dw -= base_width; + dw /= client.width_inc; + } + if (client.height_inc > 1) { + dh -= base_height; + dh /= client.height_inc; + } + + if (pw) + *pw = dw; + + if (ph) + *ph = dh; + + if (client.width_inc > 1) { + dw *= client.width_inc; + dw += base_width; + } + if (client.height_inc > 1) { + dh *= client.height_inc; + dh += base_height; + } + + frame.changing.setSize(dw, dh); + + // add the frame margin back onto frame.changing + frame.changing.setCoords(frame.changing.left() - frame.margin.left, + frame.changing.top() - frame.margin.top, + frame.changing.right() + frame.margin.right, + frame.changing.bottom() + frame.margin.bottom); + + // move frame.changing to the specified anchor + int dx = 0, + dy = 0; + switch (anchor) { + case TopLeft: + break; + + case TopRight: + dx = frame.rect.right() - frame.changing.right(); + break; + + case BottomLeft: + dy = frame.rect.bottom() - frame.changing.bottom(); + break; + + case BottomRight: + dx = frame.rect.right() - frame.changing.right(); + dy = frame.rect.bottom() - frame.changing.bottom(); + break; + + default: + assert(false); // unhandled corner + } + frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy); +} + + +void WindowStyle::doJustify(const std::string &text, int &start_pos, + unsigned int max_length, + unsigned int modifier) const { + size_t text_len = text.size(); + unsigned int length; + + do { + length = font->measureString(string(text, 0, text_len)) + modifier; + } while (length > max_length && text_len-- > 0); + + switch (justify) { + case RightJustify: + start_pos += max_length - length; + break; + + case CenterJustify: + start_pos += (max_length - length) / 2; + break; + + case LeftJustify: + default: + break; + } +} + + +BWindowGroup::BWindowGroup(Blackbox *b, Window _group) + : blackbox(b), group(_group) { + XWindowAttributes wattrib; + if (! XGetWindowAttributes(otk::OBDisplay::display, group, &wattrib)) { + // group window doesn't seem to exist anymore + delete this; + return; + } + + XSelectInput(otk::OBDisplay::display, group, + PropertyChangeMask | FocusChangeMask | StructureNotifyMask); + + blackbox->saveGroupSearch(group, this); +} + + +BWindowGroup::~BWindowGroup(void) { + blackbox->removeGroupSearch(group); +} + + +BlackboxWindow * +BWindowGroup::find(BScreen *screen, bool allow_transients) const { + BlackboxWindow *ret = blackbox->getFocusedWindow(); + + // does the focus window match (or any transient_fors)? + for (; ret; ret = ret->getTransientFor()) { + if (ret->getScreen() == screen && ret->getGroupWindow() == group && + (! ret->isTransient() || allow_transients)) + break; + } + + if (ret) return ret; + + // the focus window didn't match, look in the group's window list + BlackboxWindowList::const_iterator it, end = windowList.end(); + for (it = windowList.begin(); it != end; ++it) { + ret = *it; + if (ret->getScreen() == screen && ret->getGroupWindow() == group && + (! ret->isTransient() || allow_transients)) + break; + } + + return ret; +} + +} diff --git a/src/bbwindow.hh b/src/bbwindow.hh new file mode 100644 index 00000000..29818b43 --- /dev/null +++ b/src/bbwindow.hh @@ -0,0 +1,413 @@ +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- +#ifndef __Window_hh +#define __Window_hh + +extern "C" { +#include +#include +#ifdef SHAPE +# include +#endif // SHAPE +} + +#include + +#include "otk/timer.hh" +#include "blackbox.hh" +#include "util.hh" + +#define MwmHintsFunctions (1l << 0) +#define MwmHintsDecorations (1l << 1) + +#define MwmFuncAll (1l << 0) +#define MwmFuncResize (1l << 1) +#define MwmFuncMove (1l << 2) +#define MwmFuncIconify (1l << 3) +#define MwmFuncMaximize (1l << 4) +#define MwmFuncClose (1l << 5) + +#define MwmDecorAll (1l << 0) +#define MwmDecorBorder (1l << 1) +#define MwmDecorHandle (1l << 2) +#define MwmDecorTitle (1l << 3) +#define MwmDecorMenu (1l << 4) // not used +#define MwmDecorIconify (1l << 5) +#define MwmDecorMaximize (1l << 6) + +namespace ob { + +// this structure only contains 3 elements... the Motif 2.0 structure contains +// 5... we only need the first 3... so that is all we will define +typedef struct MwmHints { + unsigned long flags, functions, decorations; +} MwmHints; + +#define PropMwmHintsElements 3 + +class BWindowGroup { +private: + Blackbox *blackbox; + Window group; + BlackboxWindowList windowList; + +public: + BWindowGroup(Blackbox *b, Window _group); + ~BWindowGroup(void); + + inline Window groupWindow(void) const { return group; } + + inline bool empty(void) const { return windowList.empty(); } + + void addWindow(BlackboxWindow *w) { windowList.push_back(w); } + void removeWindow(BlackboxWindow *w) { windowList.remove(w); } + + /* + find a window on the specified screen. the focused window (if any) is + checked first, otherwise the first matching window found is returned. + transients are returned only if allow_transients is True. + */ + BlackboxWindow *find(BScreen *screen, bool allow_transients = False) const; +}; + + +class BlackboxWindow { +public: + enum Function { Func_Resize = (1l << 0), + Func_Move = (1l << 1), + Func_Iconify = (1l << 2), + Func_Maximize = (1l << 3), + Func_Close = (1l << 4) }; + typedef unsigned char FunctionFlags; + + enum Decoration { Decor_Titlebar = (1l << 0), + Decor_Handle = (1l << 1), + Decor_Border = (1l << 2), + Decor_Iconify = (1l << 3), + Decor_Maximize = (1l << 4), + Decor_Close = (1l << 5) }; + typedef unsigned char DecorationFlags; + + enum WindowType { Type_Desktop, + Type_Dock, + Type_Toolbar, + Type_Menu, + Type_Utility, + Type_Splash, + Type_Dialog, + Type_Normal }; + + enum Corner { TopLeft, + TopRight, + BottomLeft, + BottomRight }; + +private: + Blackbox *blackbox; + BScreen *screen; + XAtom *xatom; + otk::OBTimer *timer; + BlackboxAttributes blackbox_attrib; + + Time lastButtonPressTime; // used for double clicks, when were we clicked + + unsigned int window_number; + unsigned long current_state; + unsigned int mod_mask; // the mod mask used to grab buttons + + enum FocusMode { F_NoInput = 0, F_Passive, + F_LocallyActive, F_GloballyActive }; + FocusMode focus_mode; + + struct _flags { + bool moving, // is moving? + resizing, // is resizing? + shaded, // is shaded? + visible, // is visible? + iconic, // is iconified? + focused, // has focus? + stuck, // is omnipresent? + modal, // is modal? (must be dismissed to continue) + skip_taskbar, // skipped by taskbars? + skip_pager, // skipped by pagers? + fullscreen, // a fullscreen window? + send_focus_message, // should we send focus messages to our client? + shaped; // does the frame use the shape extension? + unsigned int maximized; // maximize is special, the number corresponds + // with a mouse button + // if 0, not maximized + // 1 = HorizVert, 2 = Vertical, 3 = Horizontal + } flags; + + struct _client { + Window window, // the client's window + window_group; + BlackboxWindow *transient_for; // which window are we a transient for? + BlackboxWindowList transientList; // which windows are our transients? + + std::string title, icon_title; + + otk::Rect rect; + Strut strut; + + int old_bw; // client's borderwidth + + unsigned int + min_width, min_height, // can not be resized smaller + max_width, max_height, // can not be resized larger + width_inc, height_inc, // increment step +#if 0 // not supported at the moment + min_aspect_x, min_aspect_y, // minimum aspect ratio + max_aspect_x, max_aspect_y, // maximum aspect ratio +#endif + base_width, base_height, + win_gravity; + + unsigned long initial_state, normal_hint_flags; + } client; + + FunctionFlags functions; + /* + * what decorations do we have? + * this is based on the type of the client window as well as user input + */ + DecorationFlags decorations; + DecorationFlags mwm_decorations; + Corner resize_dir; + WindowType window_type; + + /* + * client window = the application's window + * frame window = the window drawn around the outside of the client window + * by the window manager which contains items like the + * titlebar and close button + * title = the titlebar drawn above the client window, it displays the + * window's name and any buttons for interacting with the window, + * such as iconify, maximize, and close + * label = the window in the titlebar where the title is drawn + * buttons = maximize, iconify, close + * handle = the bar drawn at the bottom of the window, which contains the + * left and right grips used for resizing the window + * grips = the smaller reactangles in the handle, one of each side of it. + * When clicked and dragged, these resize the window interactively + * border = the line drawn around the outside edge of the frame window, + * between the title, the bordered client window, and the handle. + * Also drawn between the grips and the handle + */ + + struct _frame { + // u -> unfocused, f -> has focus, p -> pressed + unsigned long ulabel_pixel, flabel_pixel, utitle_pixel, + ftitle_pixel, uhandle_pixel, fhandle_pixel, ubutton_pixel, + fbutton_pixel, pfbutton_pixel, pubutton_pixel, + uborder_pixel, fborder_pixel, ugrip_pixel, fgrip_pixel; + Pixmap ulabel, flabel, utitle, ftitle, uhandle, fhandle, + ubutton, fbutton, pfbutton, pubutton, ugrip, fgrip; + + Window window, // the frame + plate, // holds the client + title, + label, + handle, + close_button, iconify_button, maximize_button, stick_button, + right_grip, left_grip; + + /* + * size and location of the box drawn while the window dimensions or + * location is being changed, ie. resized or moved + */ + otk::Rect changing; + + otk::Rect rect; // frame geometry + Strut margin; // margins between the frame and client + + int grab_x, grab_y; // where was the window when it was grabbed? + + unsigned int inside_w, inside_h, // window w/h without border_w + title_h, label_w, label_h, handle_h, + button_w, grip_w, mwm_border_w, border_w, + bevel_w; + } frame; + + BlackboxWindow(const BlackboxWindow&); + BlackboxWindow& operator=(const BlackboxWindow&); + + bool getState(void); + Window createToplevelWindow(); + Window createChildWindow(Window parent, unsigned long event_mask, + Cursor = None); + + bool getWindowType(void); + void updateStrut(void); + void getWMName(void); + void getWMIconName(void); + void getWMNormalHints(void); + void getWMProtocols(void); + void getWMHints(void); + void getNetWMHints(void); + void getMWMHints(void); + bool getBlackboxHints(void); + void getTransientInfo(void); + void setNetWMAttributes(void); + void associateClientWindow(void); + void decorate(void); + void decorateLabel(void); + void positionButtons(bool redecorate_label = False); + void positionWindows(void); + void createHandle(void); + void destroyHandle(void); + void createTitlebar(void); + void destroyTitlebar(void); + void createCloseButton(void); + void destroyCloseButton(void); + void createIconifyButton(void); + void destroyIconifyButton(void); + void createMaximizeButton(void); + void destroyMaximizeButton(void); + void createStickyButton(void); + void destroyStickyButton(void); + void redrawWindowFrame(void) const; + void redrawLabel(void) const; + void redrawAllButtons(void) const; + void redrawButton(bool pressed, Window win, + Pixmap fppix, unsigned long fppixel, + Pixmap uppix, unsigned long uppixel, + Pixmap fpix, unsigned long fpixel, + Pixmap upix, unsigned long upixel) const; + void redrawCloseButton(bool pressed) const; + void redrawIconifyButton(bool pressed) const; + void redrawMaximizeButton(bool pressed) const; + void redrawStickyButton(bool pressed) const; + void applyGravity(otk::Rect &r); + void restoreGravity(otk::Rect &r); + void setAllowedActions(void); + void setState(unsigned long new_state); + void upsize(void); + void doMove(int x_root, int y_root); + void doWorkspaceWarping(int x_root, int y_root, int &dx); + void doWindowSnapping(int &dx, int &dy); + void endMove(void); + void doResize(int x_root, int y_root); + void endResize(void); + + void constrain(Corner anchor, unsigned int *pw = 0, unsigned int *ph = 0); + +public: + BlackboxWindow(Blackbox *b, Window w, BScreen *s); + virtual ~BlackboxWindow(void); + + inline bool isTransient(void) const { return client.transient_for != 0; } + inline bool isFocused(void) const { return flags.focused; } + inline bool isVisible(void) const { return flags.visible; } + inline bool isIconic(void) const { return flags.iconic; } + inline bool isShaded(void) const { return flags.shaded; } + inline bool isMaximized(void) const { return flags.maximized; } + inline bool isMaximizedHoriz(void) const { return flags.maximized == 3; } + inline bool isMaximizedVert(void) const { return flags.maximized == 2; } + inline bool isMaximizedFull(void) const { return flags.maximized == 1; } + inline bool isStuck(void) const { return flags.stuck; } + inline bool isModal(void) const { return flags.modal; } + inline bool isIconifiable(void) const { return functions & Func_Iconify; } + inline bool isMaximizable(void) const { return functions & Func_Maximize; } + inline bool isResizable(void) const { return functions & Func_Resize; } + inline bool isClosable(void) const { return functions & Func_Close; } + + // is a 'normal' window? meaning, a standard client application + inline bool isNormal(void) const + { return window_type == Type_Dialog || window_type == Type_Normal || + window_type == Type_Toolbar || window_type == Type_Utility; } + inline bool isTopmost(void) const + { return window_type == Type_Toolbar || window_type == Type_Utility; } + inline bool isDesktop(void) const { return window_type == Type_Desktop; } + + inline bool hasTitlebar(void) const { return decorations & Decor_Titlebar; } + + inline const BlackboxWindowList &getTransients(void) const + { return client.transientList; } + BlackboxWindow *getTransientFor(void) const; + + inline BScreen *getScreen(void) const { return screen; } + + inline Window getFrameWindow(void) const { return frame.window; } + inline Window getClientWindow(void) const { return client.window; } + inline Window getGroupWindow(void) const { return client.window_group; } + + inline const char *getTitle(void) const + { return client.title.c_str(); } + inline const char *getIconTitle(void) const + { return client.icon_title.c_str(); } + + inline unsigned int getWorkspaceNumber(void) const + { return blackbox_attrib.workspace; } + inline unsigned int getWindowNumber(void) const { return window_number; } + + inline const otk::Rect &frameRect(void) const { return frame.rect; } + inline const otk::Rect &clientRect(void) const { return client.rect; } + + inline unsigned int getTitleHeight(void) const + { return frame.title_h; } + + inline void setWindowNumber(int n) { window_number = n; } + + bool validateClient(void) const; + bool setInputFocus(void); + + // none of these are used by the window manager, they are here to persist + // them properly in the window's netwm state property. + inline bool skipTaskbar(void) const { return flags.skip_taskbar; } + inline void setSkipTaskbar(const bool s) { flags.skip_taskbar = s; } + inline bool skipPager(void) const { return flags.skip_pager; } + inline void setSkipPager(const bool s) { flags.skip_pager = s; } + inline bool isFullscreen(void) const { return flags.fullscreen; } + inline void setFullscreen(const bool f) { flags.fullscreen = f; } + + inline void setModal(const bool m) { flags.modal = m; } + + void beginMove(int x_root, int y_root); + void beginResize(int x_root, int y_root, Corner dir); + void enableDecor(bool enable); + void setupDecor(); + void setFocusFlag(bool focus); + void iconify(void); + void deiconify(bool reassoc = True, bool raise = True); + void show(void); + void close(void); + void withdraw(void); + void maximize(unsigned int button); + void remaximize(void); + void shade(void); + void stick(void); + void reconfigure(void); + void grabButtons(void); + void ungrabButtons(void); + void installColormap(bool install); + void restore(bool remap); + void configure(int dx, int dy, unsigned int dw, unsigned int dh); + void setWorkspace(unsigned int n); + void changeBlackboxHints(const BlackboxHints *net); + void restoreAttributes(void); + + void buttonPressEvent(const XButtonEvent *be); + void buttonReleaseEvent(const XButtonEvent *re); + void motionNotifyEvent(const XMotionEvent *me); + void destroyNotifyEvent(const XDestroyWindowEvent* /*unused*/); + void mapRequestEvent(const XMapRequestEvent *mre); + void unmapNotifyEvent(const XUnmapEvent* /*unused*/); + void reparentNotifyEvent(const XReparentEvent* /*unused*/); + void propertyNotifyEvent(const XPropertyEvent *pe); + void exposeEvent(const XExposeEvent *ee); + void configureRequestEvent(const XConfigureRequestEvent *cr); + void enterNotifyEvent(const XCrossingEvent *ce); + void leaveNotifyEvent(const XCrossingEvent* /*unused*/); + +#ifdef SHAPE + void configureShape(void); + void clearShape(void); + void shapeEvent(XShapeEvent * /*unused*/); +#endif // SHAPE + + static void timeout(BlackboxWindow *t); +}; + +} + +#endif // __Window_hh diff --git a/src/window.cc b/src/window.cc deleted file mode 100644 index 17c53966..00000000 --- a/src/window.cc +++ /dev/null @@ -1,4220 +0,0 @@ -// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- - -#ifdef HAVE_CONFIG_H -# include "../config.h" -#endif // HAVE_CONFIG_H - -extern "C" { -#include -#include - -#ifdef HAVE_STRING_H -# include -#endif // HAVE_STRING_H - -#ifdef DEBUG -# ifdef HAVE_STDIO_H -# include -# endif // HAVE_STDIO_H -#endif // DEBUG - -#ifdef HAVE_STDLIB_H -# include -#endif // HAVE_STDLIB_H -} - -#include "blackbox.hh" -#include "font.hh" -#include "gccache.hh" -#include "image.hh" -#include "screen.hh" -#include "util.hh" -#include "window.hh" -#include "workspace.hh" - -using std::string; -using std::abs; - -namespace ob { - -/* - * Initializes the class with default values/the window's set initial values. - */ -BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { - // fprintf(stderr, "BlackboxWindow size: %d bytes\n", - // sizeof(BlackboxWindow)); - -#ifdef DEBUG - fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w); -#endif // DEBUG - - /* - set timer to zero... it is initialized properly later, so we check - if timer is zero in the destructor, and assume that the window is not - fully constructed if timer is zero... - */ - timer = 0; - blackbox = b; - client.window = w; - screen = s; - xatom = blackbox->getXAtom(); - - if (! validateClient()) { - delete this; - return; - } - - // fetch client size and placement - XWindowAttributes wattrib; - if (! XGetWindowAttributes(otk::OBDisplay::display, - client.window, &wattrib) || - ! wattrib.screen || wattrib.override_redirect) { -#ifdef DEBUG - fprintf(stderr, - "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n"); -#endif // DEBUG - - delete this; - return; - } - - // set the eventmask early in the game so that we make sure we get - // all the events we are interested in - XSetWindowAttributes attrib_set; - attrib_set.event_mask = PropertyChangeMask | FocusChangeMask | - StructureNotifyMask; - attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask; - XChangeWindowAttributes(otk::OBDisplay::display, client.window, - CWEventMask|CWDontPropagate, &attrib_set); - - flags.moving = flags.resizing = flags.shaded = flags.visible = - flags.iconic = flags.focused = flags.stuck = flags.modal = - flags.send_focus_message = flags.shaped = flags.skip_taskbar = - flags.skip_pager = flags.fullscreen = False; - flags.maximized = 0; - - blackbox_attrib.workspace = window_number = BSENTINEL; - - blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = 0l; - blackbox_attrib.decoration = DecorNormal; - blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0; - blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0; - - frame.border_w = 1; - frame.window = frame.plate = frame.title = frame.handle = None; - frame.close_button = frame.iconify_button = frame.maximize_button = - frame.stick_button = None; - frame.right_grip = frame.left_grip = None; - - frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel = - frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel = - frame.ubutton_pixel = frame.fbutton_pixel = frame.uborder_pixel = - frame.fborder_pixel = frame.ugrip_pixel = frame.fgrip_pixel = 0; - frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None; - frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None; - frame.ugrip = frame.fgrip = None; - - functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize; - mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border | - Decor_Iconify | Decor_Maximize; - - client.normal_hint_flags = 0; - client.window_group = None; - client.transient_for = 0; - - current_state = NormalState; - - /* - set the initial size and location of client window (relative to the - _root window_). This position is the reference point used with the - window's gravity to find the window's initial position. - */ - client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height); - client.old_bw = wattrib.border_width; - - lastButtonPressTime = 0; - - timer = new otk::OBTimer(Openbox::instance->timerManager(), - (otk::OBTimeoutHandler)timeout, - this); - timer->setTimeout(blackbox->getAutoRaiseDelay()); - - // get size, aspect, minimum/maximum size and other hints set by the - // client - - if (! getBlackboxHints()) - getNetWMHints(); - - getWMProtocols(); - getWMHints(); - getWMNormalHints(); - - frame.window = createToplevelWindow(); - - blackbox->saveWindowSearch(frame.window, this); - - frame.plate = createChildWindow(frame.window, ExposureMask); - blackbox->saveWindowSearch(frame.plate, this); - - // determine if this is a transient window - getTransientInfo(); - - // determine the window's type, so we can decide its decorations and - // functionality, or if we should not manage it at all - if (getWindowType()) { - // adjust the window decorations/behavior based on the window type - switch (window_type) { - case Type_Desktop: - case Type_Dock: - case Type_Menu: - blackbox_attrib.workspace = 0; // we do need to belong to a workspace - flags.stuck = True; // we show up on all workspaces - case Type_Splash: - // none of these windows are manipulated by the window manager - functions = 0; - break; - - case Type_Toolbar: - case Type_Utility: - // these windows get less functionality - functions &= ~(Func_Maximize | Func_Resize | Func_Iconify); - break; - - case Type_Dialog: - // dialogs cannot be maximized - functions &= ~Func_Maximize; - break; - - case Type_Normal: - // normal windows retain all of the possible decorations and - // functionality - break; - } - } else { - getMWMHints(); - } - - // further adjeust the window's decorations/behavior based on window sizes - if ((client.normal_hint_flags & PMinSize) && - (client.normal_hint_flags & PMaxSize) && - client.max_width <= client.min_width && - client.max_height <= client.min_height) { - functions &= ~(Func_Resize | Func_Maximize); - } - - setAllowedActions(); - - setupDecor(); - - if (decorations & Decor_Titlebar) - createTitlebar(); - - if (decorations & Decor_Handle) - createHandle(); - - // apply the size and gravity hint to the frame - - upsize(); - - bool place_window = True; - if (blackbox->state() == Openbox::State_Starting || isTransient() || - client.normal_hint_flags & (PPosition|USPosition)) { - applyGravity(frame.rect); - - if (blackbox->state() == Openbox::State_Starting || - client.rect.intersects(screen->getRect())) - place_window = False; - } - - // add the window's strut. note this is done *after* placing the window. - screen->addStrut(&client.strut); - updateStrut(); - - /* - the server needs to be grabbed here to prevent client's from sending - events while we are in the process of configuring their window. - We hold the grab until after we are done moving the window around. - */ - - XGrabServer(otk::OBDisplay::display); - - associateClientWindow(); - - blackbox->saveWindowSearch(client.window, this); - - if (blackbox_attrib.workspace >= screen->getWorkspaceCount()) - screen->getCurrentWorkspace()->addWindow(this, place_window); - else - screen->getWorkspace(blackbox_attrib.workspace)-> - addWindow(this, place_window); - - if (! place_window) { - // don't need to call configure if we are letting the workspace - // place the window - configure(frame.rect.x(), frame.rect.y(), - frame.rect.width(), frame.rect.height()); - - } - - positionWindows(); - - XUngrabServer(otk::OBDisplay::display); - -#ifdef SHAPE - if (blackbox->hasShapeExtensions() && flags.shaped) - configureShape(); -#endif // SHAPE - - // now that we know where to put the window and what it should look like - // we apply the decorations - decorate(); - - grabButtons(); - - XMapSubwindows(otk::OBDisplay::display, frame.window); - - // this ensures the title, buttons, and other decor are properly displayed - redrawWindowFrame(); - - // preserve the window's initial state on first map, and its current state - // across a restart - unsigned long initial_state = current_state; - if (! getState()) - current_state = initial_state; - - // get sticky state from our parent window if we've got one - if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul && - client.transient_for->isStuck() != flags.stuck) - flags.stuck = True; - - if (flags.shaded) { - flags.shaded = False; - initial_state = current_state; - shade(); - - /* - At this point in the life of a window, current_state should only be set - to IconicState if the window was an *icon*, not if it was shaded. - */ - if (initial_state != IconicState) - current_state = NormalState; - } - - if (flags.stuck) { - flags.stuck = False; - stick(); - } - - if (flags.maximized && (functions & Func_Maximize)) - remaximize(); -} - - -BlackboxWindow::~BlackboxWindow(void) { -#ifdef DEBUG - fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n", - client.window); -#endif // DEBUG - - if (! timer) // window not managed... - return; - - if (flags.moving) - endMove(); - - screen->removeStrut(&client.strut); - screen->updateAvailableArea(); - - // We don't need to worry about resizing because resizing always grabs the X - // server. This should only ever happen if using opaque moving. - if (flags.moving) - endMove(); - - delete timer; - - if (client.window_group) { - BWindowGroup *group = blackbox->searchGroup(client.window_group); - if (group) group->removeWindow(this); - } - - // remove ourselves from our transient_for - if (isTransient()) { - if (client.transient_for != (BlackboxWindow *) ~0ul) - client.transient_for->client.transientList.remove(this); - client.transient_for = (BlackboxWindow*) 0; - } - - if (client.transientList.size() > 0) { - // reset transient_for for all transients - BlackboxWindowList::iterator it, end = client.transientList.end(); - for (it = client.transientList.begin(); it != end; ++it) - (*it)->client.transient_for = (BlackboxWindow*) 0; - } - - if (frame.title) - destroyTitlebar(); - - if (frame.handle) - destroyHandle(); - - if (frame.plate) { - blackbox->removeWindowSearch(frame.plate); - XDestroyWindow(otk::OBDisplay::display, frame.plate); - } - - if (frame.window) { - blackbox->removeWindowSearch(frame.window); - XDestroyWindow(otk::OBDisplay::display, frame.window); - } - - blackbox->removeWindowSearch(client.window); -} - - -void BlackboxWindow::enableDecor(bool enable) { - blackbox_attrib.flags |= AttribDecoration; - blackbox_attrib.decoration = enable ? DecorNormal : DecorNone; - setupDecor(); - - // we can not be shaded if we lack a titlebar - if (! (decorations & Decor_Titlebar) && flags.shaded) - shade(); - - if (flags.visible && frame.window) { - XMapSubwindows(otk::OBDisplay::display, frame.window); - XMapWindow(otk::OBDisplay::display, frame.window); - } - - reconfigure(); - setState(current_state); -} - - -void BlackboxWindow::setupDecor() { - if (blackbox_attrib.decoration != DecorNone) { - // start with everything on - decorations = Decor_Close | - (mwm_decorations & Decor_Titlebar ? Decor_Titlebar : 0) | - (mwm_decorations & Decor_Border ? Decor_Border : 0) | - (mwm_decorations & Decor_Handle ? Decor_Handle : 0) | - (mwm_decorations & Decor_Iconify ? Decor_Iconify : 0) | - (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0); - - if (! (functions & Func_Close)) decorations &= ~Decor_Close; - if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize; - if (! (functions & Func_Iconify)) decorations &= ~Decor_Iconify; - if (! (functions & Func_Resize)) decorations &= ~Decor_Handle; - - switch (window_type) { - case Type_Desktop: - case Type_Dock: - case Type_Menu: - case Type_Splash: - // none of these windows are decorated by the window manager at all - decorations = 0; - break; - - case Type_Toolbar: - case Type_Utility: - decorations &= ~(Decor_Border); - break; - - case Type_Dialog: - decorations &= ~Decor_Handle; - break; - - case Type_Normal: - break; - } - } else { - decorations = 0; - } -} - -/* - * Creates a new top level window, with a given location, size, and border - * width. - * Returns: the newly created window - */ -Window BlackboxWindow::createToplevelWindow(void) { - XSetWindowAttributes attrib_create; - unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap | - CWOverrideRedirect | CWEventMask; - - attrib_create.background_pixmap = None; - attrib_create.colormap = screen->getColormap(); - attrib_create.override_redirect = True; - attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | - ButtonPress; - /* - We catch button presses because other wise they get passed down to the - root window, which will then cause root menus to show when you click the - window's frame. - */ - - return XCreateWindow(otk::OBDisplay::display, screen->getRootWindow(), - 0, 0, 1, 1, frame.border_w, screen->getDepth(), - InputOutput, screen->getVisual(), create_mask, - &attrib_create); -} - - -/* - * Creates a child window, and optionally associates a given cursor with - * the new window. - */ -Window BlackboxWindow::createChildWindow(Window parent, - unsigned long event_mask, - Cursor cursor) { - XSetWindowAttributes attrib_create; - unsigned long create_mask = CWBackPixmap | CWBorderPixel | - CWEventMask; - - attrib_create.background_pixmap = None; - attrib_create.event_mask = event_mask; - - if (cursor) { - create_mask |= CWCursor; - attrib_create.cursor = cursor; - } - - return XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0, - screen->getDepth(), InputOutput, screen->getVisual(), - create_mask, &attrib_create); -} - - -void BlackboxWindow::associateClientWindow(void) { - XSetWindowBorderWidth(otk::OBDisplay::display, client.window, 0); - getWMName(); - getWMIconName(); - - XChangeSaveSet(otk::OBDisplay::display, client.window, SetModeInsert); - - XSelectInput(otk::OBDisplay::display, frame.plate, SubstructureRedirectMask); - - /* - note we used to grab around this call to XReparentWindow however the - server is now grabbed before this method is called - */ - unsigned long event_mask = PropertyChangeMask | FocusChangeMask | - StructureNotifyMask; - XSelectInput(otk::OBDisplay::display, client.window, - event_mask & ~StructureNotifyMask); - XReparentWindow(otk::OBDisplay::display, client.window, frame.plate, 0, 0); - XSelectInput(otk::OBDisplay::display, client.window, event_mask); - - XRaiseWindow(otk::OBDisplay::display, frame.plate); - XMapSubwindows(otk::OBDisplay::display, frame.plate); - -#ifdef SHAPE - if (blackbox->hasShapeExtensions()) { - XShapeSelectInput(otk::OBDisplay::display, client.window, - ShapeNotifyMask); - - Bool shaped = False; - int foo; - unsigned int ufoo; - - XShapeQueryExtents(otk::OBDisplay::display, client.window, &shaped, - &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo, - &ufoo, &ufoo); - flags.shaped = shaped; - } -#endif // SHAPE -} - - -void BlackboxWindow::decorate(void) { - otk::BTexture* texture; - - texture = &(screen->getWindowStyle()->b_focus); - frame.fbutton = texture->render(frame.button_w, frame.button_w, - frame.fbutton); - if (! frame.fbutton) - frame.fbutton_pixel = texture->color().pixel(); - - texture = &(screen->getWindowStyle()->b_unfocus); - frame.ubutton = texture->render(frame.button_w, frame.button_w, - frame.ubutton); - if (! frame.ubutton) - frame.ubutton_pixel = texture->color().pixel(); - - unsigned char needsPressed = 0; - - texture = &(screen->getWindowStyle()->b_pressed_focus); - - if (texture->texture() != otk::BTexture::NoTexture) { - frame.pfbutton = texture->render(frame.button_w, frame.button_w, - frame.pfbutton); - if (! frame.pfbutton) - frame.pfbutton_pixel = texture->color().pixel(); - } else { - needsPressed = 0x1; - } - - texture = &(screen->getWindowStyle()->b_pressed_unfocus); - - if (texture->texture() != otk::BTexture::NoTexture) { - frame.pubutton = texture->render(frame.button_w, frame.button_w, - frame.pubutton); - if (! frame.pubutton) - frame.pubutton = texture->color().pixel(); - } else { - needsPressed |= 0x2; - } - - // if we either pressed unfocused, or pressed focused were undefined, - // make them inherit from the old resource. It's a hack for sure, but - // it allows for some backwards and forwards compatibility. - if (needsPressed) { - texture = &(screen->getWindowStyle()->b_pressed); - - if (needsPressed & 0x1) { - frame.pfbutton = texture->render(frame.button_w, frame.button_w, - frame.pfbutton); - if (! frame.pfbutton) - frame.pfbutton_pixel = texture->color().pixel(); - } - if (needsPressed & 0x2) { - frame.pubutton = texture->render(frame.button_w, frame.button_w, - frame.pubutton); - if (! frame.pubutton) - frame.pubutton = texture->color().pixel(); - } - - } - - if (decorations & Decor_Titlebar) { - texture = &(screen->getWindowStyle()->t_focus); - frame.ftitle = texture->render(frame.inside_w, frame.title_h, - frame.ftitle); - if (! frame.ftitle) - frame.ftitle_pixel = texture->color().pixel(); - - texture = &(screen->getWindowStyle()->t_unfocus); - frame.utitle = texture->render(frame.inside_w, frame.title_h, - frame.utitle); - if (! frame.utitle) - frame.utitle_pixel = texture->color().pixel(); - - XSetWindowBorder(otk::OBDisplay::display, frame.title, - screen->getBorderColor()->pixel()); - - decorateLabel(); - } - - if (decorations & Decor_Border) { - frame.fborder_pixel = screen->getWindowStyle()->f_focus.color().pixel(); - frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.color().pixel(); - } - - if (decorations & Decor_Handle) { - texture = &(screen->getWindowStyle()->h_focus); - frame.fhandle = texture->render(frame.inside_w, frame.handle_h, - frame.fhandle); - if (! frame.fhandle) - frame.fhandle_pixel = texture->color().pixel(); - - texture = &(screen->getWindowStyle()->h_unfocus); - frame.uhandle = texture->render(frame.inside_w, frame.handle_h, - frame.uhandle); - if (! frame.uhandle) - frame.uhandle_pixel = texture->color().pixel(); - - texture = &(screen->getWindowStyle()->g_focus); - frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip); - if (! frame.fgrip) - frame.fgrip_pixel = texture->color().pixel(); - - texture = &(screen->getWindowStyle()->g_unfocus); - frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip); - if (! frame.ugrip) - frame.ugrip_pixel = texture->color().pixel(); - - XSetWindowBorder(otk::OBDisplay::display, frame.handle, - screen->getBorderColor()->pixel()); - XSetWindowBorder(otk::OBDisplay::display, frame.left_grip, - screen->getBorderColor()->pixel()); - XSetWindowBorder(otk::OBDisplay::display, frame.right_grip, - screen->getBorderColor()->pixel()); - } - - XSetWindowBorder(otk::OBDisplay::display, frame.window, - screen->getBorderColor()->pixel()); -} - - -void BlackboxWindow::decorateLabel(void) { - otk::BTexture *texture; - - texture = &(screen->getWindowStyle()->l_focus); - frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel); - if (! frame.flabel) - frame.flabel_pixel = texture->color().pixel(); - - texture = &(screen->getWindowStyle()->l_unfocus); - frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel); - if (! frame.ulabel) - frame.ulabel_pixel = texture->color().pixel(); -} - - -void BlackboxWindow::createHandle(void) { - frame.handle = createChildWindow(frame.window, - ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask | ExposureMask); - blackbox->saveWindowSearch(frame.handle, this); - - frame.left_grip = - createChildWindow(frame.handle, - ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask | ExposureMask, - blackbox->getLowerLeftAngleCursor()); - blackbox->saveWindowSearch(frame.left_grip, this); - - frame.right_grip = - createChildWindow(frame.handle, - ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask | ExposureMask, - blackbox->getLowerRightAngleCursor()); - blackbox->saveWindowSearch(frame.right_grip, this); -} - - -void BlackboxWindow::destroyHandle(void) { - if (frame.fhandle) - screen->getImageControl()->removeImage(frame.fhandle); - - if (frame.uhandle) - screen->getImageControl()->removeImage(frame.uhandle); - - if (frame.fgrip) - screen->getImageControl()->removeImage(frame.fgrip); - - if (frame.ugrip) - screen->getImageControl()->removeImage(frame.ugrip); - - blackbox->removeWindowSearch(frame.left_grip); - blackbox->removeWindowSearch(frame.right_grip); - - XDestroyWindow(otk::OBDisplay::display, frame.left_grip); - XDestroyWindow(otk::OBDisplay::display, frame.right_grip); - frame.left_grip = frame.right_grip = None; - - blackbox->removeWindowSearch(frame.handle); - XDestroyWindow(otk::OBDisplay::display, frame.handle); - frame.handle = None; -} - - -void BlackboxWindow::createTitlebar(void) { - frame.title = createChildWindow(frame.window, - ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask | ExposureMask); - frame.label = createChildWindow(frame.title, - ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask | ExposureMask); - blackbox->saveWindowSearch(frame.title, this); - blackbox->saveWindowSearch(frame.label, this); - - if (decorations & Decor_Iconify) createIconifyButton(); - if (decorations & Decor_Maximize) createMaximizeButton(); - if (decorations & Decor_Close) createCloseButton(); -} - - -void BlackboxWindow::destroyTitlebar(void) { - if (frame.close_button) - destroyCloseButton(); - - if (frame.iconify_button) - destroyIconifyButton(); - - if (frame.maximize_button) - destroyMaximizeButton(); - - if (frame.stick_button) - destroyStickyButton(); - - if (frame.ftitle) - screen->getImageControl()->removeImage(frame.ftitle); - - if (frame.utitle) - screen->getImageControl()->removeImage(frame.utitle); - - if (frame.flabel) - screen->getImageControl()->removeImage(frame.flabel); - - if( frame.ulabel) - screen->getImageControl()->removeImage(frame.ulabel); - - if (frame.fbutton) - screen->getImageControl()->removeImage(frame.fbutton); - - if (frame.ubutton) - screen->getImageControl()->removeImage(frame.ubutton); - - blackbox->removeWindowSearch(frame.title); - blackbox->removeWindowSearch(frame.label); - - XDestroyWindow(otk::OBDisplay::display, frame.label); - XDestroyWindow(otk::OBDisplay::display, frame.title); - frame.title = frame.label = None; -} - - -void BlackboxWindow::createCloseButton(void) { - if (frame.title != None) { - frame.close_button = createChildWindow(frame.title, - ButtonPressMask | - ButtonReleaseMask | - ButtonMotionMask | ExposureMask); - blackbox->saveWindowSearch(frame.close_button, this); - } -} - - -void BlackboxWindow::destroyCloseButton(void) { - blackbox->removeWindowSearch(frame.close_button); - XDestroyWindow(otk::OBDisplay::display, frame.close_button); - frame.close_button = None; -} - - -void BlackboxWindow::createIconifyButton(void) { - if (frame.title != None) { - frame.iconify_button = createChildWindow(frame.title, - ButtonPressMask | - ButtonReleaseMask | - ButtonMotionMask | ExposureMask); - blackbox->saveWindowSearch(frame.iconify_button, this); - } -} - - -void BlackboxWindow::destroyIconifyButton(void) { - blackbox->removeWindowSearch(frame.iconify_button); - XDestroyWindow(otk::OBDisplay::display, frame.iconify_button); - frame.iconify_button = None; -} - - -void BlackboxWindow::createMaximizeButton(void) { - if (frame.title != None) { - frame.maximize_button = createChildWindow(frame.title, - ButtonPressMask | - ButtonReleaseMask | - ButtonMotionMask | ExposureMask); - blackbox->saveWindowSearch(frame.maximize_button, this); - } -} - - -void BlackboxWindow::destroyMaximizeButton(void) { - blackbox->removeWindowSearch(frame.maximize_button); - XDestroyWindow(otk::OBDisplay::display, frame.maximize_button); - frame.maximize_button = None; -} - -void BlackboxWindow::createStickyButton(void) { - if (frame.title != None) { - frame.stick_button = createChildWindow(frame.title, - ButtonPressMask | - ButtonReleaseMask | - ButtonMotionMask | ExposureMask); - blackbox->saveWindowSearch(frame.stick_button, this); - } -} - -void BlackboxWindow::destroyStickyButton(void) { - blackbox->removeWindowSearch(frame.stick_button); - XDestroyWindow(otk::OBDisplay::display, frame.stick_button); - frame.stick_button = None; -} - -void BlackboxWindow::positionButtons(bool redecorate_label) { - string layout = blackbox->getTitlebarLayout(); - string parsed; - - bool hasclose, hasiconify, hasmaximize, haslabel, hasstick; - hasclose = hasiconify = hasmaximize = haslabel = hasstick = false; - - string::const_iterator it, end; - for (it = layout.begin(), end = layout.end(); it != end; ++it) { - switch(*it) { - case 'C': - if (! hasclose && (decorations & Decor_Close)) { - hasclose = true; - parsed += *it; - } - break; - case 'I': - if (! hasiconify && (decorations & Decor_Iconify)) { - hasiconify = true; - parsed += *it; - } - break; - case 'S': - if (!hasstick) { - hasstick = true; - parsed += *it; - } - break; - case 'M': - if (! hasmaximize && (decorations & Decor_Maximize)) { - hasmaximize = true; - parsed += *it; - } - break; - case 'L': - if (! haslabel) { - haslabel = true; - parsed += *it; - } - break; - } - } - - if (! hasclose && frame.close_button) - destroyCloseButton(); - if (! hasiconify && frame.iconify_button) - destroyIconifyButton(); - if (! hasmaximize && frame.maximize_button) - destroyMaximizeButton(); - if (! hasstick && frame.stick_button) - destroyStickyButton(); - if (! haslabel) - parsed += 'L'; // require that the label be in the layout - - const unsigned int bsep = frame.bevel_w + 1; // separation between elements - const unsigned int by = frame.bevel_w + 1; - const unsigned int ty = frame.bevel_w; - - frame.label_w = frame.inside_w - bsep * 2 - - (frame.button_w + bsep) * (parsed.size() - 1); - - unsigned int x = bsep; - for (it = parsed.begin(), end = parsed.end(); it != end; ++it) { - switch(*it) { - case 'C': - if (! frame.close_button) createCloseButton(); - XMoveResizeWindow(otk::OBDisplay::display, frame.close_button, x, by, - frame.button_w, frame.button_w); - x += frame.button_w + bsep; - break; - case 'I': - if (! frame.iconify_button) createIconifyButton(); - XMoveResizeWindow(otk::OBDisplay::display, frame.iconify_button, x, by, - frame.button_w, frame.button_w); - x += frame.button_w + bsep; - break; - case 'S': - if (! frame.stick_button) createStickyButton(); - XMoveResizeWindow(otk::OBDisplay::display, frame.stick_button, x, by, - frame.button_w, frame.button_w); - x += frame.button_w + bsep; - break; - case 'M': - if (! frame.maximize_button) createMaximizeButton(); - XMoveResizeWindow(otk::OBDisplay::display, frame.maximize_button, x, by, - frame.button_w, frame.button_w); - x += frame.button_w + bsep; - break; - case 'L': - XMoveResizeWindow(otk::OBDisplay::display, frame.label, x, ty, - frame.label_w, frame.label_h); - x += frame.label_w + bsep; - break; - } - } - - if (redecorate_label) decorateLabel(); - redrawLabel(); - redrawAllButtons(); -} - - -void BlackboxWindow::reconfigure(void) { - restoreGravity(client.rect); - upsize(); - applyGravity(frame.rect); - positionWindows(); - decorate(); - redrawWindowFrame(); - - ungrabButtons(); - grabButtons(); -} - - -void BlackboxWindow::grabButtons(void) { - mod_mask = blackbox->getMouseModMask(); - - if (! screen->isSloppyFocus() || screen->doClickRaise()) - // grab button 1 for changing focus/raising - otk::OBDisplay::grabButton(Button1, 0, frame.plate, True, ButtonPressMask, - GrabModeSync, GrabModeSync, frame.plate, None, - screen->allowScrollLock()); - - if (functions & Func_Move) - otk::OBDisplay::grabButton(Button1, mod_mask, frame.window, True, - ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, - GrabModeAsync, frame.window, None, - screen->allowScrollLock()); - if (functions & Func_Resize) - otk::OBDisplay::grabButton(Button3, mod_mask, frame.window, True, - ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, - GrabModeAsync, frame.window, None, - screen->allowScrollLock()); - // alt+middle lowers the window - otk::OBDisplay::grabButton(Button2, mod_mask, frame.window, True, - ButtonReleaseMask, GrabModeAsync, GrabModeAsync, - frame.window, None, screen->allowScrollLock()); -} - - -void BlackboxWindow::ungrabButtons(void) { - otk::OBDisplay::ungrabButton(Button1, 0, frame.plate); - otk::OBDisplay::ungrabButton(Button1, mod_mask, frame.window); - otk::OBDisplay::ungrabButton(Button2, mod_mask, frame.window); - otk::OBDisplay::ungrabButton(Button3, mod_mask, frame.window); -} - - -void BlackboxWindow::positionWindows(void) { - XMoveResizeWindow(otk::OBDisplay::display, frame.window, - frame.rect.x(), frame.rect.y(), frame.inside_w, - (flags.shaded) ? frame.title_h : frame.inside_h); - XSetWindowBorderWidth(otk::OBDisplay::display, frame.window, - frame.border_w); - XSetWindowBorderWidth(otk::OBDisplay::display, frame.plate, - frame.mwm_border_w); - XMoveResizeWindow(otk::OBDisplay::display, frame.plate, - frame.margin.left - frame.mwm_border_w - frame.border_w, - frame.margin.top - frame.mwm_border_w - frame.border_w, - client.rect.width(), client.rect.height()); - XMoveResizeWindow(otk::OBDisplay::display, client.window, - 0, 0, client.rect.width(), client.rect.height()); - // ensure client.rect contains the real location - client.rect.setPos(frame.rect.left() + frame.margin.left, - frame.rect.top() + frame.margin.top); - - if (decorations & Decor_Titlebar) { - if (frame.title == None) createTitlebar(); - - XSetWindowBorderWidth(otk::OBDisplay::display, frame.title, - frame.border_w); - XMoveResizeWindow(otk::OBDisplay::display, frame.title, -frame.border_w, - -frame.border_w, frame.inside_w, frame.title_h); - - positionButtons(); - XMapSubwindows(otk::OBDisplay::display, frame.title); - XMapWindow(otk::OBDisplay::display, frame.title); - } else if (frame.title) { - destroyTitlebar(); - } - if (decorations & Decor_Handle) { - if (frame.handle == None) createHandle(); - XSetWindowBorderWidth(otk::OBDisplay::display, frame.handle, - frame.border_w); - XSetWindowBorderWidth(otk::OBDisplay::display, frame.left_grip, - frame.border_w); - XSetWindowBorderWidth(otk::OBDisplay::display, frame.right_grip, - frame.border_w); - - // use client.rect here so the value is correct even if shaded - XMoveResizeWindow(otk::OBDisplay::display, frame.handle, - -frame.border_w, - client.rect.height() + frame.margin.top + - frame.mwm_border_w - frame.border_w, - frame.inside_w, frame.handle_h); - XMoveResizeWindow(otk::OBDisplay::display, frame.left_grip, - -frame.border_w, -frame.border_w, - frame.grip_w, frame.handle_h); - XMoveResizeWindow(otk::OBDisplay::display, frame.right_grip, - frame.inside_w - frame.grip_w - frame.border_w, - -frame.border_w, frame.grip_w, frame.handle_h); - - XMapSubwindows(otk::OBDisplay::display, frame.handle); - XMapWindow(otk::OBDisplay::display, frame.handle); - } else if (frame.handle) { - destroyHandle(); - } - XSync(otk::OBDisplay::display, False); -} - - -void BlackboxWindow::updateStrut(void) { - unsigned long num = 4; - unsigned long *data; - if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal, - num, &data)) - return; - - if (num == 4) { - client.strut.left = data[0]; - client.strut.right = data[1]; - client.strut.top = data[2]; - client.strut.bottom = data[3]; - - screen->updateAvailableArea(); - } - - delete [] data; -} - - -bool BlackboxWindow::getWindowType(void) { - window_type = (WindowType) -1; - - unsigned long *val; - unsigned long num = (unsigned) -1; - if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom, - num, &val)) { - for (unsigned long i = 0; i < num; ++i) { - if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_desktop)) - window_type = Type_Desktop; - else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dock)) - window_type = Type_Dock; - else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_toolbar)) - window_type = Type_Toolbar; - else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_menu)) - window_type = Type_Menu; - else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_utility)) - window_type = Type_Utility; - else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_splash)) - window_type = Type_Splash; - else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dialog)) - window_type = Type_Dialog; - else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_normal)) - window_type = Type_Normal; - else if (val[i] == - xatom->getAtom(XAtom::kde_net_wm_window_type_override)) - mwm_decorations = 0; // prevent this window from getting any decor - } - delete val; - } - - if (window_type == (WindowType) -1) { - /* - * 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. - */ - if (isTransient()) - window_type = Type_Dialog; - else - window_type = Type_Normal; - - return False; - } - - return True; -} - - -void BlackboxWindow::getWMName(void) { - if (xatom->getValue(client.window, XAtom::net_wm_name, - XAtom::utf8, client.title) && - !client.title.empty()) { - xatom->eraseValue(client.window, XAtom::net_wm_visible_name); - return; - } - //fall through to using WM_NAME - if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title) - && !client.title.empty()) { - xatom->eraseValue(client.window, XAtom::net_wm_visible_name); - return; - } - // fall back to an internal default - client.title = "Unnamed"; - xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8, - client.title); - -#ifdef DEBUG_WITH_ID - // the 16 is the 8 chars of the debug text plus the number - char *tmp = new char[client.title.length() + 16]; - sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window); - client.title = tmp; - delete tmp; -#endif -} - - -void BlackboxWindow::getWMIconName(void) { - if (xatom->getValue(client.window, XAtom::net_wm_icon_name, - XAtom::utf8, client.icon_title) && - !client.icon_title.empty()) { - xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name); - return; - } - //fall through to using WM_ICON_NAME - if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi, - client.icon_title) && - !client.icon_title.empty()) { - xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name); - return; - } - // fall back to using the main name - client.icon_title = client.title; - xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8, - client.icon_title); -} - - -/* - * Retrieve which WM Protocols are supported by the client window. - * If the WM_DELETE_WINDOW protocol is supported, add the close button to the - * window's decorations and allow the close behavior. - * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates - * this. - */ -void BlackboxWindow::getWMProtocols(void) { - Atom *proto; - int num_return = 0; - - if (XGetWMProtocols(otk::OBDisplay::display, client.window, - &proto, &num_return)) { - for (int i = 0; i < num_return; ++i) { - if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) { - decorations |= Decor_Close; - functions |= Func_Close; - } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus)) - flags.send_focus_message = True; - } - - XFree(proto); - } -} - - -/* - * Gets the value of the WM_HINTS property. - * If the property is not set, then use a set of default values. - */ -void BlackboxWindow::getWMHints(void) { - focus_mode = F_Passive; - - // remove from current window group - if (client.window_group) { - BWindowGroup *group = blackbox->searchGroup(client.window_group); - if (group) group->removeWindow(this); - } - client.window_group = None; - - XWMHints *wmhint = XGetWMHints(otk::OBDisplay::display, client.window); - if (! wmhint) { - return; - } - - if (wmhint->flags & InputHint) { - if (wmhint->input == True) { - if (flags.send_focus_message) - focus_mode = F_LocallyActive; - } else { - if (flags.send_focus_message) - focus_mode = F_GloballyActive; - else - focus_mode = F_NoInput; - } - } - - if (wmhint->flags & StateHint) - current_state = wmhint->initial_state; - - if (wmhint->flags & WindowGroupHint) { - client.window_group = wmhint->window_group; - - // add window to the appropriate group - BWindowGroup *group = blackbox->searchGroup(client.window_group); - if (! group) { // no group found, create it! - new BWindowGroup(blackbox, client.window_group); - group = blackbox->searchGroup(client.window_group); - } - if (group) - group->addWindow(this); - } - - XFree(wmhint); -} - - -/* - * Gets the value of the WM_NORMAL_HINTS property. - * If the property is not set, then use a set of default values. - */ -void BlackboxWindow::getWMNormalHints(void) { - long icccm_mask; - XSizeHints sizehint; - - client.min_width = client.min_height = - client.width_inc = client.height_inc = 1; - client.base_width = client.base_height = 0; - client.win_gravity = NorthWestGravity; -#if 0 - client.min_aspect_x = client.min_aspect_y = - client.max_aspect_x = client.max_aspect_y = 1; -#endif - - // don't limit the size of a window, the default max width is the biggest - // possible - client.max_width = (unsigned) -1; - client.max_height = (unsigned) -1; - - - if (! XGetWMNormalHints(otk::OBDisplay::display, client.window, - &sizehint, &icccm_mask)) - return; - - client.normal_hint_flags = sizehint.flags; - - if (sizehint.flags & PMinSize) { - if (sizehint.min_width >= 0) - client.min_width = sizehint.min_width; - if (sizehint.min_height >= 0) - client.min_height = sizehint.min_height; - } - - if (sizehint.flags & PMaxSize) { - if (sizehint.max_width > static_cast(client.min_width)) - client.max_width = sizehint.max_width; - else - client.max_width = client.min_width; - - if (sizehint.max_height > static_cast(client.min_height)) - client.max_height = sizehint.max_height; - else - client.max_height = client.min_height; - } - - if (sizehint.flags & PResizeInc) { - client.width_inc = sizehint.width_inc; - client.height_inc = sizehint.height_inc; - } - -#if 0 // we do not support this at the moment - if (sizehint.flags & PAspect) { - client.min_aspect_x = sizehint.min_aspect.x; - client.min_aspect_y = sizehint.min_aspect.y; - client.max_aspect_x = sizehint.max_aspect.x; - client.max_aspect_y = sizehint.max_aspect.y; - } -#endif - - if (sizehint.flags & PBaseSize) { - client.base_width = sizehint.base_width; - client.base_height = sizehint.base_height; - } - - if (sizehint.flags & PWinGravity) - client.win_gravity = sizehint.win_gravity; -} - - -/* - * Gets the NETWM hints for the class' contained window. - */ -void BlackboxWindow::getNetWMHints(void) { - unsigned long workspace; - - if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, - workspace)) { - if (workspace == 0xffffffff) - flags.stuck = True; - else - blackbox_attrib.workspace = workspace; - } - - unsigned long *state; - unsigned long num = (unsigned) -1; - if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom, - num, &state)) { - bool vert = False, - horz = False; - for (unsigned long i = 0; i < num; ++i) { - if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) - flags.modal = True; - else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded)) - flags.shaded = True; - else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) - flags.skip_taskbar = True; - else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager)) - flags.skip_pager = True; - else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen)) - flags.fullscreen = True; - else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden)) - setState(IconicState); - else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert)) - vert = True; - else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz)) - horz = True; - } - if (vert && horz) - flags.maximized = 1; - else if (vert) - flags.maximized = 2; - else if (horz) - flags.maximized = 3; - - delete [] state; - } -} - - -/* - * Gets the MWM hints for the class' contained window. - * This is used while initializing the window to its first state, and not - * thereafter. - * Returns: true if the MWM hints are successfully retreived and applied; - * false if they are not. - */ -void BlackboxWindow::getMWMHints(void) { - unsigned long num; - MwmHints *mwm_hint; - - num = PropMwmHintsElements; - if (! xatom->getValue(client.window, XAtom::motif_wm_hints, - XAtom::motif_wm_hints, num, - (unsigned long **)&mwm_hint)) - return; - if (num < PropMwmHintsElements) { - delete [] mwm_hint; - return; - } - - if (mwm_hint->flags & MwmHintsDecorations) { - if (mwm_hint->decorations & MwmDecorAll) { - mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border | - Decor_Iconify | Decor_Maximize; - } else { - mwm_decorations = 0; - - if (mwm_hint->decorations & MwmDecorBorder) - mwm_decorations |= Decor_Border; - if (mwm_hint->decorations & MwmDecorHandle) - mwm_decorations |= Decor_Handle; - if (mwm_hint->decorations & MwmDecorTitle) - mwm_decorations |= Decor_Titlebar; - if (mwm_hint->decorations & MwmDecorIconify) - mwm_decorations |= Decor_Iconify; - if (mwm_hint->decorations & MwmDecorMaximize) - mwm_decorations |= Decor_Maximize; - } - } - - if (mwm_hint->flags & MwmHintsFunctions) { - if (mwm_hint->functions & MwmFuncAll) { - functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize | - Func_Close; - } else { - functions = 0; - - if (mwm_hint->functions & MwmFuncResize) - functions |= Func_Resize; - if (mwm_hint->functions & MwmFuncMove) - functions |= Func_Move; - if (mwm_hint->functions & MwmFuncIconify) - functions |= Func_Iconify; - if (mwm_hint->functions & MwmFuncMaximize) - functions |= Func_Maximize; - if (mwm_hint->functions & MwmFuncClose) - functions |= Func_Close; - } - } - delete [] mwm_hint; -} - - -/* - * Gets the blackbox hints from the class' contained window. - * This is used while initializing the window to its first state, and not - * thereafter. - * Returns: true if the hints are successfully retreived and applied; false if - * they are not. - */ -bool BlackboxWindow::getBlackboxHints(void) { - unsigned long num; - BlackboxHints *blackbox_hint; - - num = PropBlackboxHintsElements; - if (! xatom->getValue(client.window, XAtom::blackbox_hints, - XAtom::blackbox_hints, num, - (unsigned long **)&blackbox_hint)) - return False; - if (num < PropBlackboxHintsElements) { - delete [] blackbox_hint; - return False; - } - - if (blackbox_hint->flags & AttribShaded) - flags.shaded = (blackbox_hint->attrib & AttribShaded); - - if ((blackbox_hint->flags & AttribMaxHoriz) && - (blackbox_hint->flags & AttribMaxVert)) - flags.maximized = (blackbox_hint->attrib & - (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0; - else if (blackbox_hint->flags & AttribMaxVert) - flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0; - else if (blackbox_hint->flags & AttribMaxHoriz) - flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0; - - if (blackbox_hint->flags & AttribOmnipresent) - flags.stuck = (blackbox_hint->attrib & AttribOmnipresent); - - if (blackbox_hint->flags & AttribWorkspace) - blackbox_attrib.workspace = blackbox_hint->workspace; - - // if (blackbox_hint->flags & AttribStack) - // don't yet have always on top/bottom for blackbox yet... working - // on that - - if (blackbox_hint->flags & AttribDecoration) { - switch (blackbox_hint->decoration) { - case DecorNone: - blackbox_attrib.decoration = DecorNone; - break; - - case DecorTiny: - case DecorTool: - case DecorNormal: - default: - // blackbox_attrib.decoration defaults to DecorNormal - break; - } - } - - delete [] blackbox_hint; - - return True; -} - - -void BlackboxWindow::getTransientInfo(void) { - if (client.transient_for && - client.transient_for != (BlackboxWindow *) ~0ul) { - // reset transient_for in preparation of looking for a new owner - client.transient_for->client.transientList.remove(this); - } - - // we have no transient_for until we find a new one - client.transient_for = (BlackboxWindow *) 0; - - Window trans_for; - if (! XGetTransientForHint(otk::OBDisplay::display, client.window, - &trans_for)) { - // transient_for hint not set - return; - } - - if (trans_for == client.window) { - // wierd client... treat this window as a normal window - return; - } - - if (trans_for == None || trans_for == screen->getRootWindow()) { - // this is an undocumented interpretation of the ICCCM. a transient - // associated with None/Root/itself is assumed to be a modal root - // transient. we don't support the concept of a global transient, - // so we just associate this transient with nothing, and perhaps - // we will add support later for global modality. - client.transient_for = (BlackboxWindow *) ~0ul; - flags.modal = True; - return; - } - - client.transient_for = blackbox->searchWindow(trans_for); - if (! client.transient_for && - client.window_group && trans_for == client.window_group) { - // no direct transient_for, perhaps this is a group transient? - BWindowGroup *group = blackbox->searchGroup(client.window_group); - if (group) client.transient_for = group->find(screen); - } - - if (! client.transient_for || client.transient_for == this) { - // no transient_for found, or we have a wierd client that wants to be - // a transient for itself, so we treat this window as a normal window - client.transient_for = (BlackboxWindow*) 0; - return; - } - - // Check for a circular transient state: this can lock up Blackbox - // when it tries to find the non-transient window for a transient. - BlackboxWindow *w = this; - while(w->client.transient_for && - w->client.transient_for != (BlackboxWindow *) ~0ul) { - if(w->client.transient_for == this) { - client.transient_for = (BlackboxWindow*) 0; - break; - } - w = w->client.transient_for; - } - - if (client.transient_for && - client.transient_for != (BlackboxWindow *) ~0ul) { - // register ourselves with our new transient_for - client.transient_for->client.transientList.push_back(this); - flags.stuck = client.transient_for->flags.stuck; - } -} - - -BlackboxWindow *BlackboxWindow::getTransientFor(void) const { - if (client.transient_for && - client.transient_for != (BlackboxWindow*) ~0ul) - return client.transient_for; - return 0; -} - - -/* - * This function is responsible for updating both the client and the frame - * rectangles. - * According to the ICCCM a client message is not sent for a resize, only a - * move. - */ -void BlackboxWindow::configure(int dx, int dy, - unsigned int dw, unsigned int dh) { - bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) && - ! flags.moving); - - if (dw != frame.rect.width() || dh != frame.rect.height()) { - frame.rect.setRect(dx, dy, dw, dh); - frame.inside_w = frame.rect.width() - (frame.border_w * 2); - frame.inside_h = frame.rect.height() - (frame.border_w * 2); - - if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0) - frame.rect.setPos(0, 0); - - client.rect.setCoords(frame.rect.left() + frame.margin.left, - frame.rect.top() + frame.margin.top, - frame.rect.right() - frame.margin.right, - frame.rect.bottom() - frame.margin.bottom); - -#ifdef SHAPE - if (blackbox->hasShapeExtensions() && flags.shaped) { - configureShape(); - } -#endif // SHAPE - - positionWindows(); - decorate(); - redrawWindowFrame(); - } else { - frame.rect.setPos(dx, dy); - - XMoveWindow(otk::OBDisplay::display, frame.window, - frame.rect.x(), frame.rect.y()); - /* - we may have been called just after an opaque window move, so even though - the old coords match the new ones no ConfigureNotify has been sent yet. - There are likely other times when this will be relevant as well. - */ - if (! flags.moving) send_event = True; - } - - if (send_event) { - // if moving, the update and event will occur when the move finishes - client.rect.setPos(frame.rect.left() + frame.margin.left, - frame.rect.top() + frame.margin.top); - - XEvent event; - event.type = ConfigureNotify; - - event.xconfigure.display = otk::OBDisplay::display; - event.xconfigure.event = client.window; - event.xconfigure.window = client.window; - event.xconfigure.x = client.rect.x(); - event.xconfigure.y = client.rect.y(); - event.xconfigure.width = client.rect.width(); - event.xconfigure.height = client.rect.height(); - event.xconfigure.border_width = client.old_bw; - event.xconfigure.above = frame.window; - event.xconfigure.override_redirect = False; - - XSendEvent(otk::OBDisplay::display, client.window, False, - StructureNotifyMask, &event); - XFlush(otk::OBDisplay::display); - } -} - - -#ifdef SHAPE -void BlackboxWindow::configureShape(void) { - XShapeCombineShape(otk::OBDisplay::display, frame.window, ShapeBounding, - frame.margin.left - frame.border_w, - frame.margin.top - frame.border_w, - client.window, ShapeBounding, ShapeSet); - - int num = 0; - XRectangle xrect[2]; - - if (decorations & Decor_Titlebar) { - xrect[0].x = xrect[0].y = -frame.border_w; - xrect[0].width = frame.rect.width(); - xrect[0].height = frame.title_h + (frame.border_w * 2); - ++num; - } - - if (decorations & Decor_Handle) { - xrect[1].x = -frame.border_w; - xrect[1].y = frame.rect.height() - frame.margin.bottom + - frame.mwm_border_w - frame.border_w; - xrect[1].width = frame.rect.width(); - xrect[1].height = frame.handle_h + (frame.border_w * 2); - ++num; - } - - XShapeCombineRectangles(otk::OBDisplay::display, frame.window, - ShapeBounding, 0, 0, xrect, num, - ShapeUnion, Unsorted); -} - - -void BlackboxWindow::clearShape(void) { - XShapeCombineMask(otk::OBDisplay::display, frame.window, ShapeBounding, - frame.margin.left - frame.border_w, - frame.margin.top - frame.border_w, - None, ShapeSet); -} -#endif // SHAPE - - -bool BlackboxWindow::setInputFocus(void) { - if (flags.focused) return True; - - assert(flags.stuck || // window must be on the current workspace or sticky - blackbox_attrib.workspace == screen->getCurrentWorkspaceID()); - - /* - We only do this check for normal windows and dialogs because other windows - do this on purpose, such as kde's kicker, and we don't want to go moving - it. - */ - if (window_type == Type_Normal || window_type == Type_Dialog) - if (! frame.rect.intersects(screen->getRect())) { - // client is outside the screen, move it to the center - configure((screen->getWidth() - frame.rect.width()) / 2, - (screen->getHeight() - frame.rect.height()) / 2, - frame.rect.width(), frame.rect.height()); - } - - if (client.transientList.size() > 0) { - // transfer focus to any modal transients - BlackboxWindowList::iterator it, end = client.transientList.end(); - for (it = client.transientList.begin(); it != end; ++it) - if ((*it)->flags.modal) return (*it)->setInputFocus(); - } - - bool ret = True; - if (focus_mode == F_LocallyActive || focus_mode == F_Passive) { - XSetInputFocus(otk::OBDisplay::display, client.window, - RevertToPointerRoot, CurrentTime); - } else { - /* we could set the focus to none, since the window doesn't accept focus, - * but we shouldn't set focus to nothing since this would surely make - * someone angry - */ - ret = False; - } - - if (flags.send_focus_message) { - XEvent ce; - ce.xclient.type = ClientMessage; - ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols); - ce.xclient.display = otk::OBDisplay::display; - ce.xclient.window = client.window; - ce.xclient.format = 32; - ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus); - ce.xclient.data.l[1] = blackbox->getLastTime(); - ce.xclient.data.l[2] = 0l; - ce.xclient.data.l[3] = 0l; - ce.xclient.data.l[4] = 0l; - XSendEvent(otk::OBDisplay::display, client.window, False, - NoEventMask, &ce); - XFlush(otk::OBDisplay::display); - } - - return ret; -} - - -void BlackboxWindow::iconify(void) { - if (flags.iconic || ! (functions & Func_Iconify)) return; - - // We don't need to worry about resizing because resizing always grabs the X - // server. This should only ever happen if using opaque moving. - if (flags.moving) - endMove(); - - /* - * we don't want this XUnmapWindow call to generate an UnmapNotify event, so - * we need to clear the event mask on client.window for a split second. - * HOWEVER, since X11 is asynchronous, the window could be destroyed in that - * split second, leaving us with a ghost window... so, we need to do this - * while the X server is grabbed - */ - unsigned long event_mask = PropertyChangeMask | FocusChangeMask | - StructureNotifyMask; - XGrabServer(otk::OBDisplay::display); - XSelectInput(otk::OBDisplay::display, client.window, - event_mask & ~StructureNotifyMask); - XUnmapWindow(otk::OBDisplay::display, client.window); - XSelectInput(otk::OBDisplay::display, client.window, event_mask); - XUngrabServer(otk::OBDisplay::display); - - XUnmapWindow(otk::OBDisplay::display, frame.window); - flags.visible = False; - flags.iconic = True; - - setState(IconicState); - - screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this); - if (flags.stuck) { - for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) - if (i != blackbox_attrib.workspace) - screen->getWorkspace(i)->removeWindow(this, True); - } - - if (isTransient()) { - if (client.transient_for != (BlackboxWindow *) ~0ul && - ! client.transient_for->flags.iconic) { - // iconify our transient_for - client.transient_for->iconify(); - } - } - - screen->addIcon(this); - - if (client.transientList.size() > 0) { - // iconify all transients - BlackboxWindowList::iterator it, end = client.transientList.end(); - for (it = client.transientList.begin(); it != end; ++it) { - if (! (*it)->flags.iconic) (*it)->iconify(); - } - } - screen->updateStackingList(); -} - - -void BlackboxWindow::show(void) { - flags.visible = True; - flags.iconic = False; - - current_state = (flags.shaded) ? IconicState : NormalState; - setState(current_state); - - XMapWindow(otk::OBDisplay::display, client.window); - XMapSubwindows(otk::OBDisplay::display, frame.window); - XMapWindow(otk::OBDisplay::display, frame.window); - -#if 0 - int real_x, real_y; - Window child; - XTranslateCoordinates(otk::OBDisplay::display, client.window, - screen->getRootWindow(), - 0, 0, &real_x, &real_y, &child); - fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(), - client.rect.left(), client.rect.top(), real_x, real_y); - assert(client.rect.left() == real_x && client.rect.top() == real_y); -#endif -} - - -void BlackboxWindow::deiconify(bool reassoc, bool raise) { - if (flags.iconic || reassoc) - screen->reassociateWindow(this, BSENTINEL, False); - else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) - return; - - show(); - - // reassociate and deiconify all transients - if (reassoc && client.transientList.size() > 0) { - BlackboxWindowList::iterator it, end = client.transientList.end(); - for (it = client.transientList.begin(); it != end; ++it) - (*it)->deiconify(True, False); - } - - if (raise) - screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); -} - - -void BlackboxWindow::close(void) { - if (! (functions & Func_Close)) return; - - XEvent ce; - ce.xclient.type = ClientMessage; - ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols); - ce.xclient.display = otk::OBDisplay::display; - ce.xclient.window = client.window; - ce.xclient.format = 32; - ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window); - ce.xclient.data.l[1] = CurrentTime; - ce.xclient.data.l[2] = 0l; - ce.xclient.data.l[3] = 0l; - ce.xclient.data.l[4] = 0l; - XSendEvent(otk::OBDisplay::display, client.window, False, NoEventMask, &ce); - XFlush(otk::OBDisplay::display); -} - - -void BlackboxWindow::withdraw(void) { - // We don't need to worry about resizing because resizing always grabs the X - // server. This should only ever happen if using opaque moving. - if (flags.moving) - endMove(); - - flags.visible = False; - flags.iconic = False; - - setState(current_state); - - XUnmapWindow(otk::OBDisplay::display, frame.window); - - XGrabServer(otk::OBDisplay::display); - - unsigned long event_mask = PropertyChangeMask | FocusChangeMask | - StructureNotifyMask; - XSelectInput(otk::OBDisplay::display, client.window, - event_mask & ~StructureNotifyMask); - XUnmapWindow(otk::OBDisplay::display, client.window); - XSelectInput(otk::OBDisplay::display, client.window, event_mask); - - XUngrabServer(otk::OBDisplay::display); -} - - -void BlackboxWindow::maximize(unsigned int button) { - if (! (functions & Func_Maximize)) return; - - // We don't need to worry about resizing because resizing always grabs the X - // server. This should only ever happen if using opaque moving. - if (flags.moving) - endMove(); - - if (flags.maximized) { - flags.maximized = 0; - - blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert); - blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert); - - /* - when a resize finishes, maximize(0) is called to clear any maximization - flags currently set. Otherwise it still thinks it is maximized. - so we do not need to call configure() because resizing will handle it - */ - if (! flags.resizing) - configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y, - blackbox_attrib.premax_w, blackbox_attrib.premax_h); - - blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0; - blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0; - - redrawAllButtons(); // in case it is not called in configure() - setState(current_state); - return; - } - - blackbox_attrib.premax_x = frame.rect.x(); - blackbox_attrib.premax_y = frame.rect.y(); - blackbox_attrib.premax_w = frame.rect.width(); - // use client.rect so that clients can be restored even if shaded - blackbox_attrib.premax_h = - client.rect.height() + frame.margin.top + frame.margin.bottom; - -#ifdef XINERAMA - if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) { - // find the area to use - RectList availableAreas = screen->allAvailableAreas(); - RectList::iterator it, end = availableAreas.end(); - - for (it = availableAreas.begin(); it != end; ++it) - if (it->intersects(frame.rect)) break; - if (it == end) // the window isn't inside an area - it = availableAreas.begin(); // so just default to the first one - - frame.changing = *it; - } else -#endif // XINERAMA - frame.changing = screen->availableArea(); - - switch(button) { - case 1: - blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert; - blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert; - break; - - case 2: - blackbox_attrib.flags |= AttribMaxVert; - blackbox_attrib.attrib |= AttribMaxVert; - - frame.changing.setX(frame.rect.x()); - frame.changing.setWidth(frame.rect.width()); - break; - - case 3: - blackbox_attrib.flags |= AttribMaxHoriz; - blackbox_attrib.attrib |= AttribMaxHoriz; - - frame.changing.setY(frame.rect.y()); - frame.changing.setHeight(frame.rect.height()); - break; - } - - constrain(TopLeft); - - if (flags.shaded) { - blackbox_attrib.flags ^= AttribShaded; - blackbox_attrib.attrib ^= AttribShaded; - flags.shaded = False; - } - - flags.maximized = button; - - configure(frame.changing.x(), frame.changing.y(), - frame.changing.width(), frame.changing.height()); - if (flags.focused) - screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); - redrawAllButtons(); // in case it is not called in configure() - setState(current_state); -} - - -// re-maximizes the window to take into account availableArea changes -void BlackboxWindow::remaximize(void) { - if (flags.shaded) { - // we only update the window's attributes otherwise we lose the shade bit - switch(flags.maximized) { - case 1: - blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert; - blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert; - break; - - case 2: - blackbox_attrib.flags |= AttribMaxVert; - blackbox_attrib.attrib |= AttribMaxVert; - break; - - case 3: - blackbox_attrib.flags |= AttribMaxHoriz; - blackbox_attrib.attrib |= AttribMaxHoriz; - break; - } - return; - } - - // save the original dimensions because maximize will wipe them out - int premax_x = blackbox_attrib.premax_x, - premax_y = blackbox_attrib.premax_y, - premax_w = blackbox_attrib.premax_w, - premax_h = blackbox_attrib.premax_h; - - unsigned int button = flags.maximized; - flags.maximized = 0; // trick maximize() into working - maximize(button); - - // restore saved values - blackbox_attrib.premax_x = premax_x; - blackbox_attrib.premax_y = premax_y; - blackbox_attrib.premax_w = premax_w; - blackbox_attrib.premax_h = premax_h; -} - - -void BlackboxWindow::setWorkspace(unsigned int n) { - blackbox_attrib.flags |= AttribWorkspace; - blackbox_attrib.workspace = n; - if (n == BSENTINEL) { // iconified window - /* - we set the workspace to 'all workspaces' so that taskbars will show the - window. otherwise, it made uniconifying a window imposible without the - blackbox workspace menu - */ - n = 0xffffffff; - } - xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n); -} - - -void BlackboxWindow::shade(void) { - if (flags.shaded) { - XResizeWindow(otk::OBDisplay::display, frame.window, - frame.inside_w, frame.inside_h); - flags.shaded = False; - blackbox_attrib.flags ^= AttribShaded; - blackbox_attrib.attrib ^= AttribShaded; - - setState(NormalState); - - // set the frame rect to the normal size - frame.rect.setHeight(client.rect.height() + frame.margin.top + - frame.margin.bottom); - } else { - if (! (decorations & Decor_Titlebar)) - return; // can't shade it without a titlebar! - - XResizeWindow(otk::OBDisplay::display, frame.window, - frame.inside_w, frame.title_h); - flags.shaded = True; - blackbox_attrib.flags |= AttribShaded; - blackbox_attrib.attrib |= AttribShaded; - - setState(IconicState); - - // set the frame rect to the shaded size - frame.rect.setHeight(frame.title_h + (frame.border_w * 2)); - } -} - - -/* - * (Un)Sticks a window and its relatives. - */ -void BlackboxWindow::stick(void) { - if (flags.stuck) { - blackbox_attrib.flags ^= AttribOmnipresent; - blackbox_attrib.attrib ^= AttribOmnipresent; - - flags.stuck = False; - - for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) - if (i != blackbox_attrib.workspace) - screen->getWorkspace(i)->removeWindow(this, True); - - if (! flags.iconic) - screen->reassociateWindow(this, BSENTINEL, True); - // temporary fix since sticky windows suck. set the hint to what we - // actually hold in our data. - xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, - blackbox_attrib.workspace); - - setState(current_state); - } else { - flags.stuck = True; - - blackbox_attrib.flags |= AttribOmnipresent; - blackbox_attrib.attrib |= AttribOmnipresent; - - // temporary fix since sticky windows suck. set the hint to a different - // value than that contained in the class' data. - xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, - 0xffffffff); - - for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) - if (i != blackbox_attrib.workspace) - screen->getWorkspace(i)->addWindow(this, False, True); - - setState(current_state); - } - - redrawAllButtons(); - - // go up the chain - if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul && - client.transient_for->isStuck() != flags.stuck) - client.transient_for->stick(); - // go down the chain - BlackboxWindowList::iterator it; - const BlackboxWindowList::iterator end = client.transientList.end(); - for (it = client.transientList.begin(); it != end; ++it) - if ((*it)->isStuck() != flags.stuck) - (*it)->stick(); -} - - -void BlackboxWindow::redrawWindowFrame(void) const { - if (decorations & Decor_Titlebar) { - if (flags.focused) { - if (frame.ftitle) - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.title, frame.ftitle); - else - XSetWindowBackground(otk::OBDisplay::display, - frame.title, frame.ftitle_pixel); - } else { - if (frame.utitle) - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.title, frame.utitle); - else - XSetWindowBackground(otk::OBDisplay::display, - frame.title, frame.utitle_pixel); - } - XClearWindow(otk::OBDisplay::display, frame.title); - - redrawLabel(); - redrawAllButtons(); - } - - if (decorations & Decor_Handle) { - if (flags.focused) { - if (frame.fhandle) - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.handle, frame.fhandle); - else - XSetWindowBackground(otk::OBDisplay::display, - frame.handle, frame.fhandle_pixel); - - if (frame.fgrip) { - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.left_grip, frame.fgrip); - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.right_grip, frame.fgrip); - } else { - XSetWindowBackground(otk::OBDisplay::display, - frame.left_grip, frame.fgrip_pixel); - XSetWindowBackground(otk::OBDisplay::display, - frame.right_grip, frame.fgrip_pixel); - } - } else { - if (frame.uhandle) - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.handle, frame.uhandle); - else - XSetWindowBackground(otk::OBDisplay::display, - frame.handle, frame.uhandle_pixel); - - if (frame.ugrip) { - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.left_grip, frame.ugrip); - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.right_grip, frame.ugrip); - } else { - XSetWindowBackground(otk::OBDisplay::display, - frame.left_grip, frame.ugrip_pixel); - XSetWindowBackground(otk::OBDisplay::display, - frame.right_grip, frame.ugrip_pixel); - } - } - XClearWindow(otk::OBDisplay::display, frame.handle); - XClearWindow(otk::OBDisplay::display, frame.left_grip); - XClearWindow(otk::OBDisplay::display, frame.right_grip); - } - - if (decorations & Decor_Border) { - if (flags.focused) - XSetWindowBorder(otk::OBDisplay::display, - frame.plate, frame.fborder_pixel); - else - XSetWindowBorder(otk::OBDisplay::display, - frame.plate, frame.uborder_pixel); - } -} - - -void BlackboxWindow::setFocusFlag(bool focus) { - // only focus a window if it is visible - if (focus && ! flags.visible) - return; - - flags.focused = focus; - - redrawWindowFrame(); - - if (flags.focused) - blackbox->setFocusedWindow(this); -} - - -void BlackboxWindow::installColormap(bool install) { - int i = 0, ncmap = 0; - Colormap *cmaps = XListInstalledColormaps(otk::OBDisplay::display, - client.window, &ncmap); - if (cmaps) { - XWindowAttributes wattrib; - if (XGetWindowAttributes(otk::OBDisplay::display, - client.window, &wattrib)) { - if (install) { - // install the window's colormap - for (i = 0; i < ncmap; i++) { - if (*(cmaps + i) == wattrib.colormap) - // this window is using an installed color map... do not install - install = False; - } - // otherwise, install the window's colormap - if (install) - XInstallColormap(otk::OBDisplay::display, wattrib.colormap); - } else { - // uninstall the window's colormap - for (i = 0; i < ncmap; i++) { - if (*(cmaps + i) == wattrib.colormap) - // we found the colormap to uninstall - XUninstallColormap(otk::OBDisplay::display, wattrib.colormap); - } - } - } - - XFree(cmaps); - } -} - - -void BlackboxWindow::setAllowedActions(void) { - Atom actions[7]; - int num = 0; - - actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade); - actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop); - actions[num++] = xatom->getAtom(XAtom::net_wm_action_close); - - if (functions & Func_Move) - actions[num++] = xatom->getAtom(XAtom::net_wm_action_move); - if (functions & Func_Resize) - actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize); - if (functions & Func_Maximize) { - actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz); - actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert); - } - - xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom, - actions, num); -} - - -void BlackboxWindow::setState(unsigned long new_state) { - current_state = new_state; - - unsigned long state[2]; - state[0] = current_state; - state[1] = None; - xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2); - - xatom->setValue(client.window, XAtom::blackbox_attributes, - XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib, - PropBlackboxAttributesElements); - - Atom netstate[8]; - int num = 0; - if (flags.modal) - netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal); - if (flags.shaded) - netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded); - if (flags.iconic) - netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden); - if (flags.skip_taskbar) - netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar); - if (flags.skip_pager) - netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager); - if (flags.fullscreen) - netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen); - if (flags.maximized == 1 || flags.maximized == 2) - netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert); - if (flags.maximized == 1 || flags.maximized == 3) - netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz); - xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom, - netstate, num); -} - - -bool BlackboxWindow::getState(void) { - bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state, - current_state); - if (! ret) current_state = 0; - return ret; -} - - -void BlackboxWindow::restoreAttributes(void) { - unsigned long num = PropBlackboxAttributesElements; - BlackboxAttributes *net; - if (! xatom->getValue(client.window, XAtom::blackbox_attributes, - XAtom::blackbox_attributes, num, - (unsigned long **)&net)) - return; - if (num < PropBlackboxAttributesElements) { - delete [] net; - return; - } - - if (net->flags & AttribShaded && net->attrib & AttribShaded) { - flags.shaded = False; - unsigned long orig_state = current_state; - shade(); - - /* - At this point in the life of a window, current_state should only be set - to IconicState if the window was an *icon*, not if it was shaded. - */ - if (orig_state != IconicState) - current_state = WithdrawnState; - } - - if (net->workspace != screen->getCurrentWorkspaceID() && - net->workspace < screen->getWorkspaceCount()) - screen->reassociateWindow(this, net->workspace, True); - - if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) && - (blackbox_attrib.workspace < screen->getWorkspaceCount())) { - // set to WithdrawnState so it will be mapped on the new workspace - if (current_state == NormalState) current_state = WithdrawnState; - } else if (current_state == WithdrawnState) { - // the window is on this workspace and is Withdrawn, so it is waiting to - // be mapped - current_state = NormalState; - } - - if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent && - ! flags.stuck) { - stick(); - - // if the window was on another workspace, it was going to be hidden. this - // specifies that the window should be mapped since it is sticky. - if (current_state == WithdrawnState) current_state = NormalState; - } - - if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) { - int x = net->premax_x, y = net->premax_y; - unsigned int w = net->premax_w, h = net->premax_h; - flags.maximized = 0; - - unsigned int m = 0; - if ((net->flags & AttribMaxHoriz) && - (net->flags & AttribMaxVert)) - m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0; - else if (net->flags & AttribMaxVert) - m = (net->attrib & AttribMaxVert) ? 2 : 0; - else if (net->flags & AttribMaxHoriz) - m = (net->attrib & AttribMaxHoriz) ? 3 : 0; - - if (m) maximize(m); - - blackbox_attrib.premax_x = x; - blackbox_attrib.premax_y = y; - blackbox_attrib.premax_w = w; - blackbox_attrib.premax_h = h; - } - - if (net->flags & AttribDecoration) { - switch (net->decoration) { - case DecorNone: - enableDecor(False); - break; - - /* since tools only let you toggle this anyways, we'll just make that all - it supports for now. - */ - default: - case DecorNormal: - case DecorTiny: - case DecorTool: - enableDecor(True); - break; - } - } - - // with the state set it will then be the map event's job to read the - // window's state and behave accordingly - - delete [] net; -} - - -/* - * Positions the Rect r according the the client window position and - * window gravity. - */ -void BlackboxWindow::applyGravity(otk::Rect &r) { - // apply horizontal window gravity - switch (client.win_gravity) { - default: - case NorthWestGravity: - case SouthWestGravity: - case WestGravity: - r.setX(client.rect.x()); - break; - - case NorthGravity: - case SouthGravity: - case CenterGravity: - r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2); - break; - - case NorthEastGravity: - case SouthEastGravity: - case EastGravity: - r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2); - break; - - case ForgetGravity: - case StaticGravity: - r.setX(client.rect.x() - frame.margin.left); - break; - } - - // apply vertical window gravity - switch (client.win_gravity) { - default: - case NorthWestGravity: - case NorthEastGravity: - case NorthGravity: - r.setY(client.rect.y()); - break; - - case CenterGravity: - case EastGravity: - case WestGravity: - r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2); - break; - - case SouthWestGravity: - case SouthEastGravity: - case SouthGravity: - r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2); - break; - - case ForgetGravity: - case StaticGravity: - r.setY(client.rect.y() - frame.margin.top); - break; - } -} - - -/* - * The reverse of the applyGravity function. - * - * Positions the Rect r according to the frame window position and - * window gravity. - */ -void BlackboxWindow::restoreGravity(otk::Rect &r) { - // restore horizontal window gravity - switch (client.win_gravity) { - default: - case NorthWestGravity: - case SouthWestGravity: - case WestGravity: - r.setX(frame.rect.x()); - break; - - case NorthGravity: - case SouthGravity: - case CenterGravity: - r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2); - break; - - case NorthEastGravity: - case SouthEastGravity: - case EastGravity: - r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2); - break; - - case ForgetGravity: - case StaticGravity: - r.setX(frame.rect.x() + frame.margin.left); - break; - } - - // restore vertical window gravity - switch (client.win_gravity) { - default: - case NorthWestGravity: - case NorthEastGravity: - case NorthGravity: - r.setY(frame.rect.y()); - break; - - case CenterGravity: - case EastGravity: - case WestGravity: - r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2); - break; - - case SouthWestGravity: - case SouthEastGravity: - case SouthGravity: - r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2); - break; - - case ForgetGravity: - case StaticGravity: - r.setY(frame.rect.y() + frame.margin.top); - break; - } -} - - -void BlackboxWindow::redrawLabel(void) const { - if (flags.focused) { - if (frame.flabel) - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.label, frame.flabel); - else - XSetWindowBackground(otk::OBDisplay::display, - frame.label, frame.flabel_pixel); - } else { - if (frame.ulabel) - XSetWindowBackgroundPixmap(otk::OBDisplay::display, - frame.label, frame.ulabel); - else - XSetWindowBackground(otk::OBDisplay::display, - frame.label, frame.ulabel_pixel); - } - XClearWindow(otk::OBDisplay::display, frame.label); - - WindowStyle *style = screen->getWindowStyle(); - - int pos = frame.bevel_w * 2; - style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4); - style->font->drawString(frame.label, pos, 1, - (flags.focused ? style->l_text_focus : - style->l_text_unfocus), - client.title); -} - - -void BlackboxWindow::redrawAllButtons(void) const { - if (frame.iconify_button) redrawIconifyButton(False); - if (frame.maximize_button) redrawMaximizeButton(flags.maximized); - if (frame.close_button) redrawCloseButton(False); - if (frame.stick_button) redrawStickyButton(flags.stuck); -} - - -void BlackboxWindow::redrawButton(bool pressed, Window win, - Pixmap fppix, unsigned long fppixel, - Pixmap uppix, unsigned long uppixel, - Pixmap fpix, unsigned long fpixel, - Pixmap upix, unsigned long upixel) const { - Pixmap p; - unsigned long pix; - - if (pressed) { - if (flags.focused) { - p = fppix; - pix = fppixel; - } else { - p = uppix; - pix = uppixel; - } - } else { - if (flags.focused) { - p = fpix; - pix = fpixel; - } else { - p = upix; - pix = upixel; - } - } - - if (p) - XSetWindowBackgroundPixmap(otk::OBDisplay::display, win, p); - else - XSetWindowBackground(otk::OBDisplay::display, win, pix); - -} - -void BlackboxWindow::redrawIconifyButton(bool pressed) const { - redrawButton(pressed, frame.iconify_button, - frame.pfbutton, frame.pfbutton_pixel, - frame.pubutton, frame.pubutton_pixel, - frame.fbutton, frame.fbutton_pixel, - frame.ubutton, frame.ubutton_pixel); - - XClearWindow(otk::OBDisplay::display, frame.iconify_button); - otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : - screen->getWindowStyle()->b_pic_unfocus); - - PixmapMask pm = screen->getWindowStyle()->icon_button; - - if (screen->getWindowStyle()->icon_button.mask != None) { - XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask); - XSetClipOrigin(otk::OBDisplay::display, pen.gc(), - (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); - - XFillRectangle(otk::OBDisplay::display, frame.iconify_button, pen.gc(), - (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, - (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); - - XSetClipMask(otk::OBDisplay::display, pen.gc(), None); - XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0); - } else { - XDrawRectangle(otk::OBDisplay::display, frame.iconify_button, pen.gc(), - 2, (frame.button_w - 5), (frame.button_w - 5), 2); - } -} - - -void BlackboxWindow::redrawMaximizeButton(bool pressed) const { - redrawButton(pressed, frame.maximize_button, - frame.pfbutton, frame.pfbutton_pixel, - frame.pubutton, frame.pubutton_pixel, - frame.fbutton, frame.fbutton_pixel, - frame.ubutton, frame.ubutton_pixel); - - XClearWindow(otk::OBDisplay::display, frame.maximize_button); - - otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : - screen->getWindowStyle()->b_pic_unfocus); - - PixmapMask pm = screen->getWindowStyle()->max_button; - - if (pm.mask != None) { - XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask); - XSetClipOrigin(otk::OBDisplay::display, pen.gc(), - (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); - - XFillRectangle(otk::OBDisplay::display, frame.maximize_button, pen.gc(), - (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, - (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); - - XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 ); - XSetClipMask( otk::OBDisplay::display, pen.gc(), None ); - } else { - XDrawRectangle(otk::OBDisplay::display, frame.maximize_button, pen.gc(), - 2, 2, (frame.button_w - 5), (frame.button_w - 5)); - XDrawLine(otk::OBDisplay::display, frame.maximize_button, pen.gc(), - 2, 3, (frame.button_w - 3), 3); - } -} - - -void BlackboxWindow::redrawCloseButton(bool pressed) const { - redrawButton(pressed, frame.close_button, - frame.pfbutton, frame.pfbutton_pixel, - frame.pubutton, frame.pubutton_pixel, - frame.fbutton, frame.fbutton_pixel, - frame.ubutton, frame.ubutton_pixel); - - XClearWindow(otk::OBDisplay::display, frame.close_button); - - otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : - screen->getWindowStyle()->b_pic_unfocus); - - PixmapMask pm = screen->getWindowStyle()->close_button; - - if (pm.mask != None) { - XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask); - XSetClipOrigin(otk::OBDisplay::display, pen.gc(), - (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); - - XFillRectangle(otk::OBDisplay::display, frame.close_button, pen.gc(), - (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, - (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); - - - XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 ); - XSetClipMask( otk::OBDisplay::display, pen.gc(), None ); - } else { - XDrawLine(otk::OBDisplay::display, frame.close_button, pen.gc(), - 2, 2, (frame.button_w - 3), (frame.button_w - 3)); - XDrawLine(otk::OBDisplay::display, frame.close_button, pen.gc(), - 2, (frame.button_w - 3), (frame.button_w - 3), 2); - } -} - -void BlackboxWindow::redrawStickyButton(bool pressed) const { - redrawButton(pressed, frame.stick_button, - frame.pfbutton, frame.pfbutton_pixel, - frame.pubutton, frame.pubutton_pixel, - frame.fbutton, frame.fbutton_pixel, - frame.ubutton, frame.ubutton_pixel); - - XClearWindow(otk::OBDisplay::display, frame.stick_button); - - otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : - screen->getWindowStyle()->b_pic_unfocus); - - PixmapMask pm = screen->getWindowStyle()->stick_button; - - if (pm.mask != None) { - XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask); - XSetClipOrigin(otk::OBDisplay::display, pen.gc(), - (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); - - XFillRectangle(otk::OBDisplay::display, frame.stick_button, pen.gc(), - (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, - (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); - - - XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 ); - XSetClipMask( otk::OBDisplay::display, pen.gc(), None ); - } else { - XFillRectangle(otk::OBDisplay::display, frame.stick_button, pen.gc(), - frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 ); - } -} - -void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) { - if (re->window != client.window) - return; - -#ifdef DEBUG - fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n", - client.window); -#endif // DEBUG - - /* - Even though the window wants to be shown, if it is not on the current - workspace, then it isn't going to be shown right now. - */ - if (! flags.stuck && - blackbox_attrib.workspace != screen->getCurrentWorkspaceID() && - blackbox_attrib.workspace < screen->getWorkspaceCount()) - if (current_state == NormalState) current_state = WithdrawnState; - - switch (current_state) { - case IconicState: - iconify(); - break; - - case WithdrawnState: - withdraw(); - break; - - case NormalState: - case InactiveState: - case ZoomState: - default: - show(); - screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); - if (isNormal()) { - if (blackbox->state() != Openbox::State_Starting) { - XSync(otk::OBDisplay::display, False); // make sure the frame is mapped - if (screen->doFocusNew() || (isTransient() && getTransientFor() && - getTransientFor()->isFocused())) { - setInputFocus(); - } - if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) { - int x, y, rx, ry; - Window c, r; - unsigned int m; - XQueryPointer(otk::OBDisplay::display, screen->getRootWindow(), - &r, &c, &rx, &ry, &x, &y, &m); - beginMove(rx, ry); - } - } - } - break; - } -} - - -void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) { - if (ue->window != client.window) - return; - -#ifdef DEBUG - fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n", - client.window); -#endif // DEBUG - - screen->unmanageWindow(this, False); -} - - -void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) { - if (de->window != client.window) - return; - -#ifdef DEBUG - fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n", - client.window); -#endif // DEBUG - - screen->unmanageWindow(this, False); -} - - -void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) { - if (re->window != client.window || re->parent == frame.plate) - return; - -#ifdef DEBUG - fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to " - "0x%lx.\n", client.window, re->parent); -#endif // DEBUG - - XEvent ev; - ev.xreparent = *re; - XPutBackEvent(otk::OBDisplay::display, &ev); - screen->unmanageWindow(this, True); -} - - -void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) { - if (pe->state == PropertyDelete || ! validateClient()) - return; - -#if 0 - fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n", - client.window); -#endif - - switch(pe->atom) { - case XA_WM_CLASS: - case XA_WM_CLIENT_MACHINE: - case XA_WM_COMMAND: - break; - - case XA_WM_TRANSIENT_FOR: { - bool s = flags.stuck; - - // determine if this is a transient window - getTransientInfo(); - - if (flags.stuck != s) stick(); - - // adjust the window decorations based on transience - if (isTransient()) { - functions &= ~Func_Maximize; - setAllowedActions(); - setupDecor(); - } - - reconfigure(); - } - break; - - case XA_WM_HINTS: - getWMHints(); - break; - - case XA_WM_ICON_NAME: - getWMIconName(); - if (flags.iconic) screen->propagateWindowName(this); - break; - - case XAtom::net_wm_name: - case XA_WM_NAME: - getWMName(); - - if (decorations & Decor_Titlebar) - redrawLabel(); - - screen->propagateWindowName(this); - break; - - case XA_WM_NORMAL_HINTS: { - getWMNormalHints(); - - if ((client.normal_hint_flags & PMinSize) && - (client.normal_hint_flags & PMaxSize)) { - // the window now can/can't resize itself, so the buttons need to be - // regrabbed. - ungrabButtons(); - if (client.max_width <= client.min_width && - client.max_height <= client.min_height) { - functions &= ~(Func_Resize | Func_Maximize); - } else { - if (! isTransient()) - functions |= Func_Maximize; - functions |= Func_Resize; - } - grabButtons(); - setAllowedActions(); - setupDecor(); - } - - otk::Rect old_rect = frame.rect; - - upsize(); - - if (old_rect != frame.rect) - reconfigure(); - - break; - } - - default: - if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) { - getWMProtocols(); - - if ((decorations & Decor_Close) && (! frame.close_button)) { - createCloseButton(); - if (decorations & Decor_Titlebar) { - positionButtons(True); - XMapSubwindows(otk::OBDisplay::display, frame.title); - } - } - } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) { - updateStrut(); - } - - break; - } -} - - -void BlackboxWindow::exposeEvent(const XExposeEvent *ee) { -#if 0 - fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window); -#endif - - if (frame.label == ee->window && (decorations & Decor_Titlebar)) - redrawLabel(); - else if (frame.close_button == ee->window) - redrawCloseButton(False); - else if (frame.maximize_button == ee->window) - redrawMaximizeButton(flags.maximized); - else if (frame.iconify_button == ee->window) - redrawIconifyButton(False); - else if (frame.stick_button == ee->window) - redrawStickyButton(flags.stuck); -} - - -void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) { - if (cr->window != client.window || flags.iconic) - return; - - if (cr->value_mask & CWBorderWidth) - client.old_bw = cr->border_width; - - if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) { - frame.changing = frame.rect; - - if (cr->value_mask & (CWX | CWY)) { - if (cr->value_mask & CWX) - client.rect.setX(cr->x); - if (cr->value_mask & CWY) - client.rect.setY(cr->y); - - applyGravity(frame.changing); - } - - if (cr->value_mask & (CWWidth | CWHeight)) { - if (cr->value_mask & CWWidth) - frame.changing.setWidth(cr->width + - frame.margin.left + frame.margin.right); - - if (cr->value_mask & CWHeight) - frame.changing.setHeight(cr->height + - frame.margin.top + frame.margin.bottom); - - /* - if a position change has been specified, then that position will be - used instead of determining a position based on the window's gravity. - */ - if (! (cr->value_mask & (CWX | CWY))) { - Corner corner; - switch (client.win_gravity) { - case NorthEastGravity: - case EastGravity: - corner = TopRight; - break; - case SouthWestGravity: - case SouthGravity: - corner = BottomLeft; - break; - case SouthEastGravity: - corner = BottomRight; - break; - default: // NorthWest, Static, etc - corner = TopLeft; - } - constrain(corner); - } - } - - configure(frame.changing.x(), frame.changing.y(), - frame.changing.width(), frame.changing.height()); - } - - if (cr->value_mask & CWStackMode && !isDesktop()) { - switch (cr->detail) { - case Below: - case BottomIf: - screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); - break; - - case Above: - case TopIf: - default: - screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); - break; - } - } -} - - -void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) { -#ifdef DEBUG - fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n", - client.window); -#endif - - if (frame.maximize_button == be->window && be->button <= 3) { - redrawMaximizeButton(True); - } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) { - if (! flags.focused) - setInputFocus(); - - if (frame.iconify_button == be->window) { - redrawIconifyButton(True); - } else if (frame.close_button == be->window) { - redrawCloseButton(True); - } else if (frame.stick_button == be->window) { - redrawStickyButton(True); - } else if (frame.plate == be->window) { - screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); - - XAllowEvents(otk::OBDisplay::display, ReplayPointer, be->time); - } else { - if (frame.title == be->window || frame.label == be->window) { - if (((be->time - lastButtonPressTime) <= - blackbox->getDoubleClickInterval()) || - (be->state == ControlMask)) { - lastButtonPressTime = 0; - shade(); - } else { - lastButtonPressTime = be->time; - } - } - - screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); - } - } else if (be->button == 2 && (be->window != frame.iconify_button) && - (be->window != frame.close_button) && - (be->window != frame.stick_button)) { - screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); - // mouse wheel up - } else if (be->button == 4) { - if ((be->window == frame.label || - be->window == frame.title || - be->window == frame.maximize_button || - be->window == frame.iconify_button || - be->window == frame.close_button || - be->window == frame.stick_button) && - ! flags.shaded) - shade(); - // mouse wheel down - } else if (be->button == 5) { - if ((be->window == frame.label || - be->window == frame.title || - be->window == frame.maximize_button || - be->window == frame.iconify_button || - be->window == frame.close_button || - be->window == frame.stick_button) && - flags.shaded) - shade(); - } -} - - -void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) { -#ifdef DEBUG - fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n", - client.window); -#endif - - if (re->window == frame.maximize_button && - re->button >= 1 && re->button <= 3) { - if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && - (re->y >= 0 && re->y <= static_cast(frame.button_w))) { - maximize(re->button); - } else { - redrawMaximizeButton(flags.maximized); - } - } else if (re->window == frame.iconify_button && re->button == 1) { - if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && - (re->y >= 0 && re->y <= static_cast(frame.button_w))) { - iconify(); - } else { - redrawIconifyButton(False); - } - } else if (re->window == frame.stick_button && re->button == 1) { - if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && - (re->y >= 0 && re->y <= static_cast(frame.button_w))) { - stick(); - } else { - redrawStickyButton(False); - } - } else if (re->window == frame.close_button & re->button == 1) { - if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && - (re->y >= 0 && re->y <= static_cast(frame.button_w))) - close(); - redrawCloseButton(False); - } else if (flags.moving) { - endMove(); - } else if (flags.resizing) { - endResize(); - } else if (re->window == frame.window) { - if (re->button == 2 && re->state == mod_mask) - XUngrabPointer(otk::OBDisplay::display, CurrentTime); - } -} - - - -void BlackboxWindow::beginMove(int x_root, int y_root) { - if (! (functions & Func_Move)) return; - - assert(! (flags.resizing || flags.moving)); - - /* - Only one window can be moved/resized at a time. If another window is already - being moved or resized, then stop it before whating to work with this one. - */ - BlackboxWindow *changing = blackbox->getChangingWindow(); - if (changing && changing != this) { - if (changing->flags.moving) - changing->endMove(); - else // if (changing->flags.resizing) - changing->endResize(); - } - - XGrabPointer(otk::OBDisplay::display, frame.window, False, - PointerMotionMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, - None, blackbox->getMoveCursor(), CurrentTime); - - flags.moving = True; - blackbox->setChangingWindow(this); - - if (! screen->doOpaqueMove()) { - XGrabServer(otk::OBDisplay::display); - - frame.changing = frame.rect; - screen->showPosition(frame.changing.x(), frame.changing.y()); - - XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), - screen->getOpGC(), - frame.changing.x(), - frame.changing.y(), - frame.changing.width() - 1, - frame.changing.height() - 1); - } - - frame.grab_x = x_root - frame.rect.x() - frame.border_w; - frame.grab_y = y_root - frame.rect.y() - frame.border_w; -} - - -void BlackboxWindow::doMove(int x_root, int y_root) { - assert(flags.moving); - assert(blackbox->getChangingWindow() == this); - - int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y; - dx -= frame.border_w; - dy -= frame.border_w; - - doWindowSnapping(dx, dy); - - if (screen->doOpaqueMove()) { - if (screen->doWorkspaceWarping()) - doWorkspaceWarping(x_root, y_root, dx); - - configure(dx, dy, frame.rect.width(), frame.rect.height()); - } else { - XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), - screen->getOpGC(), - frame.changing.x(), - frame.changing.y(), - frame.changing.width() - 1, - frame.changing.height() - 1); - - if (screen->doWorkspaceWarping()) - doWorkspaceWarping(x_root, y_root, dx); - - frame.changing.setPos(dx, dy); - - XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), - screen->getOpGC(), - frame.changing.x(), - frame.changing.y(), - frame.changing.width() - 1, - frame.changing.height() - 1); - } - - screen->showPosition(dx, dy); -} - - -void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) { - // workspace warping - bool warp = False; - unsigned int dest = screen->getCurrentWorkspaceID(); - if (x_root <= 0) { - warp = True; - - if (dest > 0) dest--; - else dest = screen->getNumberOfWorkspaces() - 1; - - } else if (x_root >= screen->getRect().right()) { - warp = True; - - if (dest < screen->getNumberOfWorkspaces() - 1) dest++; - else dest = 0; - } - if (! warp) - return; - - bool focus = flags.focused; // had focus while moving? - - int dest_x = x_root; - if (x_root <= 0) { - dest_x += screen->getRect().width() - 1; - dx += screen->getRect().width() - 1; - } else { - dest_x -= screen->getRect().width() - 1; - dx -= screen->getRect().width() - 1; - } - - if (! flags.stuck) - screen->reassociateWindow(this, dest, False); - screen->changeWorkspaceID(dest); - - if (screen->doOpaqueMove()) - XGrabServer(otk::OBDisplay::display); - - XUngrabPointer(otk::OBDisplay::display, CurrentTime); - XWarpPointer(otk::OBDisplay::display, None, - screen->getRootWindow(), 0, 0, 0, 0, - dest_x, y_root); - XGrabPointer(otk::OBDisplay::display, frame.window, False, - PointerMotionMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, - None, blackbox->getMoveCursor(), CurrentTime); - - if (screen->doOpaqueMove()) - XUngrabServer(otk::OBDisplay::display); - - if (focus) - setInputFocus(); - -} - - -void BlackboxWindow::doWindowSnapping(int &dx, int &dy) { - // how much resistance to edges to provide - const int resistance_size = screen->getResistanceSize(); - - // how far away to snap - const int snap_distance = screen->getSnapThreshold(); - - // how to snap windows - const int snap_to_windows = screen->getWindowToWindowSnap(); - const int snap_to_edges = screen->getWindowToEdgeSnap(); - // the amount of space away from the edge to provide resistance/snap - const int snap_offset = screen->getSnapOffset(); - - // find the geomeetery where the moving window currently is - const otk::Rect &moving = - screen->doOpaqueMove() ? frame.rect : frame.changing; - - // window corners - const int wleft = dx, - wright = dx + frame.rect.width() - 1, - wtop = dy, - wbottom = dy + frame.rect.height() - 1; - - if (snap_to_windows) { - otk::RectList rectlist; - - Workspace *w = screen->getWorkspace(getWorkspaceNumber()); - assert(w); - - // add windows on the workspace to the rect list - const BlackboxWindowList& stack_list = w->getStackingList(); - BlackboxWindowList::const_iterator st_it, st_end = stack_list.end(); - for (st_it = stack_list.begin(); st_it != st_end; ++st_it) - if (*st_it != this) // don't snap to ourself - rectlist.push_back( (*st_it)->frameRect() ); - - otk::RectList::const_iterator it, end = rectlist.end(); - for (it = rectlist.begin(); it != end; ++it) { - bool snapped = False; - const otk::Rect &winrect = *it; - otk::Rect offsetrect; - offsetrect.setCoords(winrect.left() - snap_offset, - winrect.top() - snap_offset, - winrect.right() + snap_offset, - winrect.bottom() + snap_offset); - - if (snap_to_windows == BScreen::WindowResistance) - // if the window is already over top of this snap target, then - // resistance is futile, so just ignore it - if (winrect.intersects(moving)) - continue; - - int dleft, dright, dtop, dbottom; - - // if the windows are in the same plane vertically - if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) && - wtop < (signed)(winrect.y() + winrect.height() - 1)) { - - if (snap_to_windows == BScreen::WindowResistance) { - dleft = wright - offsetrect.left(); - dright = offsetrect.right() - wleft; - - // snap left of other window? - if (dleft >= 0 && dleft < resistance_size && - dleft < (wright - wleft)) { - dx = offsetrect.left() - frame.rect.width(); - snapped = True; - } - // snap right of other window? - else if (dright >= 0 && dright < resistance_size && - dright < (wright - wleft)) { - dx = offsetrect.right() + 1; - snapped = True; - } - } else { // BScreen::WindowSnap - dleft = abs(wright - offsetrect.left()); - dright = abs(wleft - offsetrect.right()); - - // snap left of other window? - if (dleft < snap_distance && dleft <= dright) { - dx = offsetrect.left() - frame.rect.width(); - snapped = True; - } - // snap right of other window? - else if (dright < snap_distance) { - dx = offsetrect.right() + 1; - snapped = True; - } - } - - if (snapped) { - if (screen->getWindowCornerSnap()) { - // try corner-snap to its other sides - if (snap_to_windows == BScreen::WindowResistance) { - dtop = winrect.top() - wtop; - dbottom = wbottom - winrect.bottom(); - if (dtop > 0 && dtop < resistance_size) { - // if we're already past the top edge, then don't provide - // resistance - if (moving.top() >= winrect.top()) - dy = winrect.top(); - } else if (dbottom > 0 && dbottom < resistance_size) { - // if we're already past the bottom edge, then don't provide - // resistance - if (moving.bottom() <= winrect.bottom()) - dy = winrect.bottom() - frame.rect.height() + 1; - } - } else { // BScreen::WindowSnap - dtop = abs(wtop - winrect.top()); - dbottom = abs(wbottom - winrect.bottom()); - if (dtop < snap_distance && dtop <= dbottom) - dy = winrect.top(); - else if (dbottom < snap_distance) - dy = winrect.bottom() - frame.rect.height() + 1; - } - } - - continue; - } - } - - // if the windows are on the same plane horizontally - if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) && - wleft < (signed)(winrect.x() + winrect.width() - 1)) { - - if (snap_to_windows == BScreen::WindowResistance) { - dtop = wbottom - offsetrect.top(); - dbottom = offsetrect.bottom() - wtop; - - // snap top of other window? - if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) { - dy = offsetrect.top() - frame.rect.height(); - snapped = True; - } - // snap bottom of other window? - else if (dbottom >= 0 && dbottom < resistance_size && - dbottom < (wbottom - wtop)) { - dy = offsetrect.bottom() + 1; - snapped = True; - } - } else { // BScreen::WindowSnap - dtop = abs(wbottom - offsetrect.top()); - dbottom = abs(wtop - offsetrect.bottom()); - - // snap top of other window? - if (dtop < snap_distance && dtop <= dbottom) { - dy = offsetrect.top() - frame.rect.height(); - snapped = True; - } - // snap bottom of other window? - else if (dbottom < snap_distance) { - dy = offsetrect.bottom() + 1; - snapped = True; - } - - } - - if (snapped) { - if (screen->getWindowCornerSnap()) { - // try corner-snap to its other sides - if (snap_to_windows == BScreen::WindowResistance) { - dleft = winrect.left() - wleft; - dright = wright - winrect.right(); - if (dleft > 0 && dleft < resistance_size) { - // if we're already past the left edge, then don't provide - // resistance - if (moving.left() >= winrect.left()) - dx = winrect.left(); - } else if (dright > 0 && dright < resistance_size) { - // if we're already past the right edge, then don't provide - // resistance - if (moving.right() <= winrect.right()) - dx = winrect.right() - frame.rect.width() + 1; - } - } else { // BScreen::WindowSnap - dleft = abs(wleft - winrect.left()); - dright = abs(wright - winrect.right()); - if (dleft < snap_distance && dleft <= dright) - dx = winrect.left(); - else if (dright < snap_distance) - dx = winrect.right() - frame.rect.width() + 1; - } - } - - continue; - } - } - } - } - - if (snap_to_edges) { - otk::RectList rectlist; - - // snap to the screen edges (and screen boundaries for xinerama) -#ifdef XINERAMA - if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) { - rectlist.insert(rectlist.begin(), - screen->getXineramaAreas().begin(), - screen->getXineramaAreas().end()); - } else -#endif // XINERAMA - rectlist.push_back(screen->getRect()); - - otk::RectList::const_iterator it, end = rectlist.end(); - for (it = rectlist.begin(); it != end; ++it) { - const otk::Rect &srect = *it; - otk::Rect offsetrect; - offsetrect.setCoords(srect.left() + snap_offset, - srect.top() + snap_offset, - srect.right() - snap_offset, - srect.bottom() - snap_offset); - - if (snap_to_edges == BScreen::WindowResistance) { - // if we're not in the rectangle then don't snap to it. - if (! srect.contains(moving)) - continue; - } else { // BScreen::WindowSnap - // if we're not in the rectangle then don't snap to it. - if (! srect.intersects(otk::Rect(wleft, wtop, frame.rect.width(), - frame.rect.height()))) - continue; - } - - if (snap_to_edges == BScreen::WindowResistance) { - int dleft = offsetrect.left() - wleft, - dright = wright - offsetrect.right(), - dtop = offsetrect.top() - wtop, - dbottom = wbottom - offsetrect.bottom(); - - // snap left? - if (dleft > 0 && dleft < resistance_size) - dx = offsetrect.left(); - // snap right? - else if (dright > 0 && dright < resistance_size) - dx = offsetrect.right() - frame.rect.width() + 1; - - // snap top? - if (dtop > 0 && dtop < resistance_size) - dy = offsetrect.top(); - // snap bottom? - else if (dbottom > 0 && dbottom < resistance_size) - dy = offsetrect.bottom() - frame.rect.height() + 1; - } else { // BScreen::WindowSnap - int dleft = abs(wleft - offsetrect.left()), - dright = abs(wright - offsetrect.right()), - dtop = abs(wtop - offsetrect.top()), - dbottom = abs(wbottom - offsetrect.bottom()); - - // snap left? - if (dleft < snap_distance && dleft <= dright) - dx = offsetrect.left(); - // snap right? - else if (dright < snap_distance) - dx = offsetrect.right() - frame.rect.width() + 1; - - // snap top? - if (dtop < snap_distance && dtop <= dbottom) - dy = offsetrect.top(); - // snap bottom? - else if (dbottom < snap_distance) - dy = offsetrect.bottom() - frame.rect.height() + 1; - } - } - } -} - - -void BlackboxWindow::endMove(void) { - assert(flags.moving); - assert(blackbox->getChangingWindow() == this); - - flags.moving = False; - blackbox->setChangingWindow(0); - - if (! screen->doOpaqueMove()) { - /* when drawing the rubber band, we need to make sure we only draw inside - * the frame... frame.changing_* contain the new coords for the window, - * so we need to subtract 1 from changing_w/changing_h every where we - * draw the rubber band (for both moving and resizing) - */ - XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), - screen->getOpGC(), frame.changing.x(), frame.changing.y(), - frame.changing.width() - 1, frame.changing.height() - 1); - XUngrabServer(otk::OBDisplay::display); - - configure(frame.changing.x(), frame.changing.y(), - frame.changing.width(), frame.changing.height()); - } else { - configure(frame.rect.x(), frame.rect.y(), - frame.rect.width(), frame.rect.height()); - } - screen->hideGeometry(); - - XUngrabPointer(otk::OBDisplay::display, CurrentTime); - - // if there are any left over motions from the move, drop them now - XSync(otk::OBDisplay::display, false); // make sure we don't miss any - XEvent e; - while (XCheckTypedWindowEvent(otk::OBDisplay::display, frame.window, - MotionNotify, &e)); -} - - -void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) { - if (! (functions & Func_Resize)) return; - - assert(! (flags.resizing || flags.moving)); - - /* - Only one window can be moved/resized at a time. If another window is - already being moved or resized, then stop it before whating to work with - this one. - */ - BlackboxWindow *changing = blackbox->getChangingWindow(); - if (changing && changing != this) { - if (changing->flags.moving) - changing->endMove(); - else // if (changing->flags.resizing) - changing->endResize(); - } - - resize_dir = dir; - - Cursor cursor; - Corner anchor; - - switch (resize_dir) { - case BottomLeft: - anchor = TopRight; - cursor = blackbox->getLowerLeftAngleCursor(); - break; - - case BottomRight: - anchor = TopLeft; - cursor = blackbox->getLowerRightAngleCursor(); - break; - - case TopLeft: - anchor = BottomRight; - cursor = blackbox->getUpperLeftAngleCursor(); - break; - - case TopRight: - anchor = BottomLeft; - cursor = blackbox->getUpperRightAngleCursor(); - break; - - default: - assert(false); // unhandled Corner - return; // unreachable, for the compiler - } - - XGrabServer(otk::OBDisplay::display); - XGrabPointer(otk::OBDisplay::display, frame.window, False, - PointerMotionMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime); - - flags.resizing = True; - blackbox->setChangingWindow(this); - - unsigned int gw, gh; - frame.changing = frame.rect; - - constrain(anchor, &gw, &gh); - - XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), - screen->getOpGC(), frame.changing.x(), frame.changing.y(), - frame.changing.width() - 1, frame.changing.height() - 1); - - screen->showGeometry(gw, gh); - - frame.grab_x = x_root; - frame.grab_y = y_root; -} - - -void BlackboxWindow::doResize(int x_root, int y_root) { - assert(flags.resizing); - assert(blackbox->getChangingWindow() == this); - - XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), - screen->getOpGC(), frame.changing.x(), frame.changing.y(), - frame.changing.width() - 1, frame.changing.height() - 1); - - unsigned int gw, gh; - Corner anchor; - int dx, dy; // the amount of change in the size of the window - - switch (resize_dir) { - case BottomLeft: - anchor = TopRight; - dx = - (x_root - frame.grab_x); - dy = + (y_root - frame.grab_y); - break; - case BottomRight: - anchor = TopLeft; - dx = + (x_root - frame.grab_x); - dy = + (y_root - frame.grab_y); - break; - case TopLeft: - anchor = BottomRight; - dx = - (x_root - frame.grab_x); - dy = - (y_root - frame.grab_y); - break; - case TopRight: - anchor = BottomLeft; - dx = + (x_root - frame.grab_x); - dy = - (y_root - frame.grab_y); - break; - - default: - assert(false); // unhandled Corner - return; // unreachable, for the compiler - } - - // make sure the user cant resize the window smaller than 0, which makes it - // wrap around and become huge - if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width(); - if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height(); - - frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy); - - constrain(anchor, &gw, &gh); - - XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), - screen->getOpGC(), frame.changing.x(), frame.changing.y(), - frame.changing.width() - 1, frame.changing.height() - 1); - - screen->showGeometry(gw, gh); -} - - -void BlackboxWindow::endResize(void) { - assert(flags.resizing); - assert(blackbox->getChangingWindow() == this); - - XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(), - screen->getOpGC(), frame.changing.x(), frame.changing.y(), - frame.changing.width() - 1, frame.changing.height() - 1); - XUngrabServer(otk::OBDisplay::display); - - // unset maximized state after resized when fully maximized - if (flags.maximized == 1) - maximize(0); - - flags.resizing = False; - blackbox->setChangingWindow(0); - - configure(frame.changing.x(), frame.changing.y(), - frame.changing.width(), frame.changing.height()); - screen->hideGeometry(); - - XUngrabPointer(otk::OBDisplay::display, CurrentTime); - - // if there are any left over motions from the resize, drop them now - XSync(otk::OBDisplay::display, false); // make sure we don't miss any - XEvent e; - while (XCheckTypedWindowEvent(otk::OBDisplay::display, frame.window, - MotionNotify, &e)); -} - - -void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) { -#if 0 - fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n", - client.window); -#endif - - if (flags.moving) { - doMove(me->x_root, me->y_root); - } else if (flags.resizing) { - doResize(me->x_root, me->y_root); - } else { - if ((functions & Func_Move) && - (me->state & Button1Mask) && - (frame.title == me->window || frame.label == me->window || - frame.handle == me->window || frame.window == me->window)) { - beginMove(me->x_root, me->y_root); - } else if ((functions & Func_Resize) && - ((me->state & Button1Mask) && - (me->window == frame.right_grip || - me->window == frame.left_grip)) || - ((me->state & Button3Mask) && (me->state & mod_mask) && - (frame.title == me->window || frame.label == me->window || - frame.handle == me->window || frame.window == me->window || - frame.right_grip == me->window || - frame.left_grip == me->window))) { - unsigned int zones = screen->getResizeZones(); - Corner corner; - - if (me->window == frame.left_grip) { - corner = BottomLeft; - } else if (me->window == frame.right_grip || zones == 1) { - corner = BottomRight; - } else { - bool top; - bool left = (me->x_root - frame.rect.x() <= - static_cast(frame.rect.width() / 2)); - if (zones == 2) - top = False; - else // (zones == 4) - top = (me->y_root - frame.rect.y() <= - static_cast(frame.rect.height() / 2)); - corner = (top ? (left ? TopLeft : TopRight) : - (left ? BottomLeft : BottomRight)); - } - - beginResize(me->x_root, me->y_root, corner); - } - } -} - - -void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) { - if (! (screen->isSloppyFocus() && isVisible() && isNormal())) - return; - - XEvent e; - bool leave = False, inferior = False; - - while (XCheckTypedWindowEvent(otk::OBDisplay::display, ce->window, - LeaveNotify, &e)) { - if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) { - leave = True; - inferior = (e.xcrossing.detail == NotifyInferior); - } - } - - if (! leave || inferior) { - if (! isFocused()) { - bool success = setInputFocus(); - if (success) // if focus succeeded install the colormap - installColormap(True); // XXX: shouldnt we honour no install? - - /* - We only auto-raise when the window wasn't focused because otherwise - we run into problems with gtk+ drop-down lists. The window ends up - raising over the list. - */ - if (screen->doAutoRaise()) - timer->start(); - } - } -} - - -void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) { - if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal())) - return; - - installColormap(False); - - timer->stop(); -} - - -#ifdef SHAPE -void BlackboxWindow::shapeEvent(XShapeEvent *e) { - if (blackbox->hasShapeExtensions()) { - if (! e->shaped && flags.shaped) { - clearShape(); - flags.shaped = False; - } else if (e->shaped) { - configureShape(); - flags.shaped = True; - } - } -} -#endif // SHAPE - - -bool BlackboxWindow::validateClient(void) const { - XSync(otk::OBDisplay::display, False); - - XEvent e; - if (XCheckTypedWindowEvent(otk::OBDisplay::display, client.window, - DestroyNotify, &e) || - XCheckTypedWindowEvent(otk::OBDisplay::display, client.window, - UnmapNotify, &e)) { - XPutBackEvent(otk::OBDisplay::display, &e); - - return False; - } - - return True; -} - - -void BlackboxWindow::restore(bool remap) { - XChangeSaveSet(otk::OBDisplay::display, client.window, SetModeDelete); - XSelectInput(otk::OBDisplay::display, client.window, NoEventMask); - XSelectInput(otk::OBDisplay::display, frame.plate, NoEventMask); - - // do not leave a shaded window as an icon unless it was an icon - if (flags.shaded && ! flags.iconic) - setState(NormalState); - - // erase the netwm stuff that we read when a window maps, so that it - // doesn't persist between mappings. - // (these are the ones read in getNetWMFlags().) - xatom->eraseValue(client.window, XAtom::net_wm_desktop); - xatom->eraseValue(client.window, XAtom::net_wm_state); - - restoreGravity(client.rect); - - XUnmapWindow(otk::OBDisplay::display, frame.window); - XUnmapWindow(otk::OBDisplay::display, client.window); - - XSetWindowBorderWidth(otk::OBDisplay::display, client.window, client.old_bw); - - XEvent ev; - if (XCheckTypedWindowEvent(otk::OBDisplay::display, client.window, - ReparentNotify, &ev)) { - remap = True; - } else { - // according to the ICCCM - if the client doesn't reparent to - // root, then we have to do it for them - XReparentWindow(otk::OBDisplay::display, client.window, - screen->getRootWindow(), - client.rect.x(), client.rect.y()); - } - - if (remap) XMapWindow(otk::OBDisplay::display, client.window); -} - - -// timer for autoraise -void BlackboxWindow::timeout(BlackboxWindow *t) { - t->screen->getWorkspace(t->blackbox_attrib.workspace)->raiseWindow(t); - printf("TIMED OUT YA YAY\n"); -} - - -void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) { - if ((net->flags & AttribShaded) && - ((blackbox_attrib.attrib & AttribShaded) != - (net->attrib & AttribShaded))) - shade(); - - if (flags.visible && // watch out for requests when we can not be seen - (net->flags & (AttribMaxVert | AttribMaxHoriz)) && - ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) != - (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) { - if (flags.maximized) { - maximize(0); - } else { - int button = 0; - - if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert)) - button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0); - else if (net->flags & AttribMaxVert) - button = ((net->attrib & AttribMaxVert) ? 2 : 0); - else if (net->flags & AttribMaxHoriz) - button = ((net->attrib & AttribMaxHoriz) ? 3 : 0); - - maximize(button); - } - } - - if ((net->flags & AttribOmnipresent) && - ((blackbox_attrib.attrib & AttribOmnipresent) != - (net->attrib & AttribOmnipresent))) - stick(); - - if ((net->flags & AttribWorkspace) && - (blackbox_attrib.workspace != net->workspace)) { - screen->reassociateWindow(this, net->workspace, True); - - if (screen->getCurrentWorkspaceID() != net->workspace) { - withdraw(); - } else { - show(); - screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); - } - } - - if (net->flags & AttribDecoration) { - switch (net->decoration) { - case DecorNone: - enableDecor(False); - break; - - default: - case DecorNormal: - case DecorTiny: - case DecorTool: - enableDecor(True); - break; - } - } -} - - -/* - * Set the sizes of all components of the window frame - * (the window decorations). - * These values are based upon the current style settings and the client - * window's dimensions. - */ -void BlackboxWindow::upsize(void) { - frame.bevel_w = screen->getBevelWidth(); - - if (decorations & Decor_Border) { - frame.border_w = screen->getBorderWidth(); - if (! isTransient()) - frame.mwm_border_w = screen->getFrameWidth(); - else - frame.mwm_border_w = 0; - } else { - frame.mwm_border_w = frame.border_w = 0; - } - - if (decorations & Decor_Titlebar) { - // the height of the titlebar is based upon the height of the font being - // used to display the window's title - WindowStyle *style = screen->getWindowStyle(); - frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2; - - frame.label_h = frame.title_h - (frame.bevel_w * 2); - frame.button_w = (frame.label_h - 2); - - // set the top frame margin - frame.margin.top = frame.border_w + frame.title_h + - frame.border_w + frame.mwm_border_w; - } else { - frame.title_h = 0; - frame.label_h = 0; - frame.button_w = 0; - - // set the top frame margin - frame.margin.top = frame.border_w + frame.mwm_border_w; - } - - // set the left/right frame margin - frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w; - - if (decorations & Decor_Handle) { - frame.grip_w = frame.button_w * 2; - frame.handle_h = screen->getHandleWidth(); - - // set the bottom frame margin - frame.margin.bottom = frame.border_w + frame.handle_h + - frame.border_w + frame.mwm_border_w; - } else { - frame.handle_h = 0; - frame.grip_w = 0; - - // set the bottom frame margin - frame.margin.bottom = frame.border_w + frame.mwm_border_w; - } - - /* - We first get the normal dimensions and use this to define the inside_w/h - then we modify the height if shading is in effect. - If the shade state is not considered then frame.rect gets reset to the - normal window size on a reconfigure() call resulting in improper - dimensions appearing in move/resize and other events. - */ - unsigned int - height = client.rect.height() + frame.margin.top + frame.margin.bottom, - width = client.rect.width() + frame.margin.left + frame.margin.right; - - frame.inside_w = width - (frame.border_w * 2); - frame.inside_h = height - (frame.border_w * 2); - - if (flags.shaded) - height = frame.title_h + (frame.border_w * 2); - frame.rect.setSize(width, height); -} - - -/* - * Calculate the size of the client window and constrain it to the - * size specified by the size hints of the client window. - * - * The logical width and height are placed into pw and ph, if they - * are non-zero. Logical size refers to the users perception of - * the window size (for example an xterm resizes in cells, not in pixels). - * pw and ph are then used to display the geometry during window moves, resize, - * etc. - * - * The physical geometry is placed into frame.changing_{x,y,width,height}. - * Physical geometry refers to the geometry of the window in pixels. - */ -void BlackboxWindow::constrain(Corner anchor, - unsigned int *pw, unsigned int *ph) { - // frame.changing represents the requested frame size, we need to - // strip the frame margin off and constrain the client size - frame.changing.setCoords(frame.changing.left() + frame.margin.left, - frame.changing.top() + frame.margin.top, - frame.changing.right() - frame.margin.right, - frame.changing.bottom() - frame.margin.bottom); - - unsigned int dw = frame.changing.width(), dh = frame.changing.height(), - base_width = (client.base_width) ? client.base_width : client.min_width, - base_height = (client.base_height) ? client.base_height : - client.min_height; - - // constrain, but only if the min/max are being used. if they aren't, then - // this resize is going to be from a ConfigureRequest because the window - // isn't allowed to be resized by the user. And in that case, we don't want - // to limit what the app can do - if (client.max_width > client.min_width || - client.max_height > client.min_height) { - if (dw < client.min_width) dw = client.min_width; - if (dh < client.min_height) dh = client.min_height; - if (dw > client.max_width) dw = client.max_width; - if (dh > client.max_height) dh = client.max_height; - } - - if (client.width_inc > 1) { - dw -= base_width; - dw /= client.width_inc; - } - if (client.height_inc > 1) { - dh -= base_height; - dh /= client.height_inc; - } - - if (pw) - *pw = dw; - - if (ph) - *ph = dh; - - if (client.width_inc > 1) { - dw *= client.width_inc; - dw += base_width; - } - if (client.height_inc > 1) { - dh *= client.height_inc; - dh += base_height; - } - - frame.changing.setSize(dw, dh); - - // add the frame margin back onto frame.changing - frame.changing.setCoords(frame.changing.left() - frame.margin.left, - frame.changing.top() - frame.margin.top, - frame.changing.right() + frame.margin.right, - frame.changing.bottom() + frame.margin.bottom); - - // move frame.changing to the specified anchor - int dx = 0, - dy = 0; - switch (anchor) { - case TopLeft: - break; - - case TopRight: - dx = frame.rect.right() - frame.changing.right(); - break; - - case BottomLeft: - dy = frame.rect.bottom() - frame.changing.bottom(); - break; - - case BottomRight: - dx = frame.rect.right() - frame.changing.right(); - dy = frame.rect.bottom() - frame.changing.bottom(); - break; - - default: - assert(false); // unhandled corner - } - frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy); -} - - -void WindowStyle::doJustify(const std::string &text, int &start_pos, - unsigned int max_length, - unsigned int modifier) const { - size_t text_len = text.size(); - unsigned int length; - - do { - length = font->measureString(string(text, 0, text_len)) + modifier; - } while (length > max_length && text_len-- > 0); - - switch (justify) { - case RightJustify: - start_pos += max_length - length; - break; - - case CenterJustify: - start_pos += (max_length - length) / 2; - break; - - case LeftJustify: - default: - break; - } -} - - -BWindowGroup::BWindowGroup(Blackbox *b, Window _group) - : blackbox(b), group(_group) { - XWindowAttributes wattrib; - if (! XGetWindowAttributes(otk::OBDisplay::display, group, &wattrib)) { - // group window doesn't seem to exist anymore - delete this; - return; - } - - XSelectInput(otk::OBDisplay::display, group, - PropertyChangeMask | FocusChangeMask | StructureNotifyMask); - - blackbox->saveGroupSearch(group, this); -} - - -BWindowGroup::~BWindowGroup(void) { - blackbox->removeGroupSearch(group); -} - - -BlackboxWindow * -BWindowGroup::find(BScreen *screen, bool allow_transients) const { - BlackboxWindow *ret = blackbox->getFocusedWindow(); - - // does the focus window match (or any transient_fors)? - for (; ret; ret = ret->getTransientFor()) { - if (ret->getScreen() == screen && ret->getGroupWindow() == group && - (! ret->isTransient() || allow_transients)) - break; - } - - if (ret) return ret; - - // the focus window didn't match, look in the group's window list - BlackboxWindowList::const_iterator it, end = windowList.end(); - for (it = windowList.begin(); it != end; ++it) { - ret = *it; - if (ret->getScreen() == screen && ret->getGroupWindow() == group && - (! ret->isTransient() || allow_transients)) - break; - } - - return ret; -} - -} diff --git a/src/window.hh b/src/window.hh deleted file mode 100644 index 29818b43..00000000 --- a/src/window.hh +++ /dev/null @@ -1,413 +0,0 @@ -// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- -#ifndef __Window_hh -#define __Window_hh - -extern "C" { -#include -#include -#ifdef SHAPE -# include -#endif // SHAPE -} - -#include - -#include "otk/timer.hh" -#include "blackbox.hh" -#include "util.hh" - -#define MwmHintsFunctions (1l << 0) -#define MwmHintsDecorations (1l << 1) - -#define MwmFuncAll (1l << 0) -#define MwmFuncResize (1l << 1) -#define MwmFuncMove (1l << 2) -#define MwmFuncIconify (1l << 3) -#define MwmFuncMaximize (1l << 4) -#define MwmFuncClose (1l << 5) - -#define MwmDecorAll (1l << 0) -#define MwmDecorBorder (1l << 1) -#define MwmDecorHandle (1l << 2) -#define MwmDecorTitle (1l << 3) -#define MwmDecorMenu (1l << 4) // not used -#define MwmDecorIconify (1l << 5) -#define MwmDecorMaximize (1l << 6) - -namespace ob { - -// this structure only contains 3 elements... the Motif 2.0 structure contains -// 5... we only need the first 3... so that is all we will define -typedef struct MwmHints { - unsigned long flags, functions, decorations; -} MwmHints; - -#define PropMwmHintsElements 3 - -class BWindowGroup { -private: - Blackbox *blackbox; - Window group; - BlackboxWindowList windowList; - -public: - BWindowGroup(Blackbox *b, Window _group); - ~BWindowGroup(void); - - inline Window groupWindow(void) const { return group; } - - inline bool empty(void) const { return windowList.empty(); } - - void addWindow(BlackboxWindow *w) { windowList.push_back(w); } - void removeWindow(BlackboxWindow *w) { windowList.remove(w); } - - /* - find a window on the specified screen. the focused window (if any) is - checked first, otherwise the first matching window found is returned. - transients are returned only if allow_transients is True. - */ - BlackboxWindow *find(BScreen *screen, bool allow_transients = False) const; -}; - - -class BlackboxWindow { -public: - enum Function { Func_Resize = (1l << 0), - Func_Move = (1l << 1), - Func_Iconify = (1l << 2), - Func_Maximize = (1l << 3), - Func_Close = (1l << 4) }; - typedef unsigned char FunctionFlags; - - enum Decoration { Decor_Titlebar = (1l << 0), - Decor_Handle = (1l << 1), - Decor_Border = (1l << 2), - Decor_Iconify = (1l << 3), - Decor_Maximize = (1l << 4), - Decor_Close = (1l << 5) }; - typedef unsigned char DecorationFlags; - - enum WindowType { Type_Desktop, - Type_Dock, - Type_Toolbar, - Type_Menu, - Type_Utility, - Type_Splash, - Type_Dialog, - Type_Normal }; - - enum Corner { TopLeft, - TopRight, - BottomLeft, - BottomRight }; - -private: - Blackbox *blackbox; - BScreen *screen; - XAtom *xatom; - otk::OBTimer *timer; - BlackboxAttributes blackbox_attrib; - - Time lastButtonPressTime; // used for double clicks, when were we clicked - - unsigned int window_number; - unsigned long current_state; - unsigned int mod_mask; // the mod mask used to grab buttons - - enum FocusMode { F_NoInput = 0, F_Passive, - F_LocallyActive, F_GloballyActive }; - FocusMode focus_mode; - - struct _flags { - bool moving, // is moving? - resizing, // is resizing? - shaded, // is shaded? - visible, // is visible? - iconic, // is iconified? - focused, // has focus? - stuck, // is omnipresent? - modal, // is modal? (must be dismissed to continue) - skip_taskbar, // skipped by taskbars? - skip_pager, // skipped by pagers? - fullscreen, // a fullscreen window? - send_focus_message, // should we send focus messages to our client? - shaped; // does the frame use the shape extension? - unsigned int maximized; // maximize is special, the number corresponds - // with a mouse button - // if 0, not maximized - // 1 = HorizVert, 2 = Vertical, 3 = Horizontal - } flags; - - struct _client { - Window window, // the client's window - window_group; - BlackboxWindow *transient_for; // which window are we a transient for? - BlackboxWindowList transientList; // which windows are our transients? - - std::string title, icon_title; - - otk::Rect rect; - Strut strut; - - int old_bw; // client's borderwidth - - unsigned int - min_width, min_height, // can not be resized smaller - max_width, max_height, // can not be resized larger - width_inc, height_inc, // increment step -#if 0 // not supported at the moment - min_aspect_x, min_aspect_y, // minimum aspect ratio - max_aspect_x, max_aspect_y, // maximum aspect ratio -#endif - base_width, base_height, - win_gravity; - - unsigned long initial_state, normal_hint_flags; - } client; - - FunctionFlags functions; - /* - * what decorations do we have? - * this is based on the type of the client window as well as user input - */ - DecorationFlags decorations; - DecorationFlags mwm_decorations; - Corner resize_dir; - WindowType window_type; - - /* - * client window = the application's window - * frame window = the window drawn around the outside of the client window - * by the window manager which contains items like the - * titlebar and close button - * title = the titlebar drawn above the client window, it displays the - * window's name and any buttons for interacting with the window, - * such as iconify, maximize, and close - * label = the window in the titlebar where the title is drawn - * buttons = maximize, iconify, close - * handle = the bar drawn at the bottom of the window, which contains the - * left and right grips used for resizing the window - * grips = the smaller reactangles in the handle, one of each side of it. - * When clicked and dragged, these resize the window interactively - * border = the line drawn around the outside edge of the frame window, - * between the title, the bordered client window, and the handle. - * Also drawn between the grips and the handle - */ - - struct _frame { - // u -> unfocused, f -> has focus, p -> pressed - unsigned long ulabel_pixel, flabel_pixel, utitle_pixel, - ftitle_pixel, uhandle_pixel, fhandle_pixel, ubutton_pixel, - fbutton_pixel, pfbutton_pixel, pubutton_pixel, - uborder_pixel, fborder_pixel, ugrip_pixel, fgrip_pixel; - Pixmap ulabel, flabel, utitle, ftitle, uhandle, fhandle, - ubutton, fbutton, pfbutton, pubutton, ugrip, fgrip; - - Window window, // the frame - plate, // holds the client - title, - label, - handle, - close_button, iconify_button, maximize_button, stick_button, - right_grip, left_grip; - - /* - * size and location of the box drawn while the window dimensions or - * location is being changed, ie. resized or moved - */ - otk::Rect changing; - - otk::Rect rect; // frame geometry - Strut margin; // margins between the frame and client - - int grab_x, grab_y; // where was the window when it was grabbed? - - unsigned int inside_w, inside_h, // window w/h without border_w - title_h, label_w, label_h, handle_h, - button_w, grip_w, mwm_border_w, border_w, - bevel_w; - } frame; - - BlackboxWindow(const BlackboxWindow&); - BlackboxWindow& operator=(const BlackboxWindow&); - - bool getState(void); - Window createToplevelWindow(); - Window createChildWindow(Window parent, unsigned long event_mask, - Cursor = None); - - bool getWindowType(void); - void updateStrut(void); - void getWMName(void); - void getWMIconName(void); - void getWMNormalHints(void); - void getWMProtocols(void); - void getWMHints(void); - void getNetWMHints(void); - void getMWMHints(void); - bool getBlackboxHints(void); - void getTransientInfo(void); - void setNetWMAttributes(void); - void associateClientWindow(void); - void decorate(void); - void decorateLabel(void); - void positionButtons(bool redecorate_label = False); - void positionWindows(void); - void createHandle(void); - void destroyHandle(void); - void createTitlebar(void); - void destroyTitlebar(void); - void createCloseButton(void); - void destroyCloseButton(void); - void createIconifyButton(void); - void destroyIconifyButton(void); - void createMaximizeButton(void); - void destroyMaximizeButton(void); - void createStickyButton(void); - void destroyStickyButton(void); - void redrawWindowFrame(void) const; - void redrawLabel(void) const; - void redrawAllButtons(void) const; - void redrawButton(bool pressed, Window win, - Pixmap fppix, unsigned long fppixel, - Pixmap uppix, unsigned long uppixel, - Pixmap fpix, unsigned long fpixel, - Pixmap upix, unsigned long upixel) const; - void redrawCloseButton(bool pressed) const; - void redrawIconifyButton(bool pressed) const; - void redrawMaximizeButton(bool pressed) const; - void redrawStickyButton(bool pressed) const; - void applyGravity(otk::Rect &r); - void restoreGravity(otk::Rect &r); - void setAllowedActions(void); - void setState(unsigned long new_state); - void upsize(void); - void doMove(int x_root, int y_root); - void doWorkspaceWarping(int x_root, int y_root, int &dx); - void doWindowSnapping(int &dx, int &dy); - void endMove(void); - void doResize(int x_root, int y_root); - void endResize(void); - - void constrain(Corner anchor, unsigned int *pw = 0, unsigned int *ph = 0); - -public: - BlackboxWindow(Blackbox *b, Window w, BScreen *s); - virtual ~BlackboxWindow(void); - - inline bool isTransient(void) const { return client.transient_for != 0; } - inline bool isFocused(void) const { return flags.focused; } - inline bool isVisible(void) const { return flags.visible; } - inline bool isIconic(void) const { return flags.iconic; } - inline bool isShaded(void) const { return flags.shaded; } - inline bool isMaximized(void) const { return flags.maximized; } - inline bool isMaximizedHoriz(void) const { return flags.maximized == 3; } - inline bool isMaximizedVert(void) const { return flags.maximized == 2; } - inline bool isMaximizedFull(void) const { return flags.maximized == 1; } - inline bool isStuck(void) const { return flags.stuck; } - inline bool isModal(void) const { return flags.modal; } - inline bool isIconifiable(void) const { return functions & Func_Iconify; } - inline bool isMaximizable(void) const { return functions & Func_Maximize; } - inline bool isResizable(void) const { return functions & Func_Resize; } - inline bool isClosable(void) const { return functions & Func_Close; } - - // is a 'normal' window? meaning, a standard client application - inline bool isNormal(void) const - { return window_type == Type_Dialog || window_type == Type_Normal || - window_type == Type_Toolbar || window_type == Type_Utility; } - inline bool isTopmost(void) const - { return window_type == Type_Toolbar || window_type == Type_Utility; } - inline bool isDesktop(void) const { return window_type == Type_Desktop; } - - inline bool hasTitlebar(void) const { return decorations & Decor_Titlebar; } - - inline const BlackboxWindowList &getTransients(void) const - { return client.transientList; } - BlackboxWindow *getTransientFor(void) const; - - inline BScreen *getScreen(void) const { return screen; } - - inline Window getFrameWindow(void) const { return frame.window; } - inline Window getClientWindow(void) const { return client.window; } - inline Window getGroupWindow(void) const { return client.window_group; } - - inline const char *getTitle(void) const - { return client.title.c_str(); } - inline const char *getIconTitle(void) const - { return client.icon_title.c_str(); } - - inline unsigned int getWorkspaceNumber(void) const - { return blackbox_attrib.workspace; } - inline unsigned int getWindowNumber(void) const { return window_number; } - - inline const otk::Rect &frameRect(void) const { return frame.rect; } - inline const otk::Rect &clientRect(void) const { return client.rect; } - - inline unsigned int getTitleHeight(void) const - { return frame.title_h; } - - inline void setWindowNumber(int n) { window_number = n; } - - bool validateClient(void) const; - bool setInputFocus(void); - - // none of these are used by the window manager, they are here to persist - // them properly in the window's netwm state property. - inline bool skipTaskbar(void) const { return flags.skip_taskbar; } - inline void setSkipTaskbar(const bool s) { flags.skip_taskbar = s; } - inline bool skipPager(void) const { return flags.skip_pager; } - inline void setSkipPager(const bool s) { flags.skip_pager = s; } - inline bool isFullscreen(void) const { return flags.fullscreen; } - inline void setFullscreen(const bool f) { flags.fullscreen = f; } - - inline void setModal(const bool m) { flags.modal = m; } - - void beginMove(int x_root, int y_root); - void beginResize(int x_root, int y_root, Corner dir); - void enableDecor(bool enable); - void setupDecor(); - void setFocusFlag(bool focus); - void iconify(void); - void deiconify(bool reassoc = True, bool raise = True); - void show(void); - void close(void); - void withdraw(void); - void maximize(unsigned int button); - void remaximize(void); - void shade(void); - void stick(void); - void reconfigure(void); - void grabButtons(void); - void ungrabButtons(void); - void installColormap(bool install); - void restore(bool remap); - void configure(int dx, int dy, unsigned int dw, unsigned int dh); - void setWorkspace(unsigned int n); - void changeBlackboxHints(const BlackboxHints *net); - void restoreAttributes(void); - - void buttonPressEvent(const XButtonEvent *be); - void buttonReleaseEvent(const XButtonEvent *re); - void motionNotifyEvent(const XMotionEvent *me); - void destroyNotifyEvent(const XDestroyWindowEvent* /*unused*/); - void mapRequestEvent(const XMapRequestEvent *mre); - void unmapNotifyEvent(const XUnmapEvent* /*unused*/); - void reparentNotifyEvent(const XReparentEvent* /*unused*/); - void propertyNotifyEvent(const XPropertyEvent *pe); - void exposeEvent(const XExposeEvent *ee); - void configureRequestEvent(const XConfigureRequestEvent *cr); - void enterNotifyEvent(const XCrossingEvent *ce); - void leaveNotifyEvent(const XCrossingEvent* /*unused*/); - -#ifdef SHAPE - void configureShape(void); - void clearShape(void); - void shapeEvent(XShapeEvent * /*unused*/); -#endif // SHAPE - - static void timeout(BlackboxWindow *t); -}; - -} - -#endif // __Window_hh -- cgit v1.2.3