// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- // Window.cc for Blackbox - an X11 Window manager // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry // Copyright (c) 1997 - 2000, 2002 Brad Hughes // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #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 } #include #include "i18n.hh" #include "blackbox.hh" #include "GCCache.hh" #include "Iconmenu.hh" #include "Image.hh" #include "Screen.hh" #include "Toolbar.hh" #include "Util.hh" #include "Window.hh" #include "Windowmenu.hh" #include "Workspace.hh" #include "Slit.hh" using std::string; /* * 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; } // 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(blackbox->getXDisplay(), client.window, CWEventMask|CWDontPropagate, &attrib_set); // fetch client size and placement XWindowAttributes wattrib; if ((! XGetWindowAttributes(blackbox->getXDisplay(), client.window, &wattrib)) || (! wattrib.screen) || wattrib.override_redirect) { #ifdef DEBUG fprintf(stderr, "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n"); #endif // DEBUG delete this; return; } flags.moving = flags.resizing = flags.shaded = flags.visible = flags.iconic = flags.focused = flags.stuck = flags.modal = flags.send_focus_message = flags.shaped = False; flags.maximized = 0; blackbox_attrib.workspace = window_number = BSENTINEL; blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = blackbox_attrib.decoration = 0l; 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 = 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.pbutton_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.pbutton = frame.ugrip = frame.fgrip = decorations; decorations = Decor_Titlebar | Decor_Border | Decor_Handle | Decor_Iconify | Decor_Maximize; functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize; client.wm_hint_flags = client.normal_hint_flags = 0; client.transient_for = 0; /* get 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; windowmenu = 0; lastButtonPressTime = 0; timer = new BTimer(blackbox, this); timer->setTimeout(blackbox->getAutoRaiseDelay()); if (! getBlackboxHints()) getMWMHints(); // get size, aspect, minimum/maximum size and other hints set by the // client getWMProtocols(); getWMHints(); getWMNormalHints(); if (client.initial_state == WithdrawnState) { screen->getSlit()->addClient(client.window); delete this; return; } frame.window = createToplevelWindow(); frame.plate = createChildWindow(frame.window); associateClientWindow(); blackbox->saveWindowSearch(frame.window, this); blackbox->saveWindowSearch(frame.plate, this); blackbox->saveWindowSearch(client.window, this); // determine if this is a transient window getTransientInfo(); // adjust the window decorations based on transience and window sizes if (isTransient()) { decorations &= ~(Decor_Maximize | Decor_Handle); functions &= ~Func_Maximize; } if ((client.normal_hint_flags & PMinSize) && (client.normal_hint_flags & PMaxSize) && client.max_width <= client.min_width && client.max_height <= client.min_height) { decorations &= ~(Decor_Maximize | Decor_Handle); functions &= ~(Func_Resize | Func_Maximize); } upsize(); bool place_window = True; if (blackbox->isStartup() || isTransient() || client.normal_hint_flags & (PPosition|USPosition)) { setGravityOffsets(); if (blackbox->isStartup() || client.rect.intersects(screen->availableArea())) place_window = False; } if (decorations & Decor_Titlebar) createTitlebar(); if (decorations & Decor_Handle) createHandle(); #ifdef SHAPE if (blackbox->hasShapeExtensions() && flags.shaped) { configureShape(); } #endif // SHAPE if ((! screen->isSloppyFocus()) || screen->doClickRaise()) { // grab button 1 for changing focus/raising blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask, GrabModeSync, GrabModeSync, frame.plate, None); } blackbox->grabButton(Button1, Mod1Mask, frame.window, True, ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, GrabModeAsync, frame.window, blackbox->getMoveCursor()); blackbox->grabButton(Button2, Mod1Mask, frame.window, True, ButtonReleaseMask, GrabModeAsync, GrabModeAsync, frame.window, None); blackbox->grabButton(Button3, Mod1Mask, frame.window, True, ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, GrabModeAsync, frame.window, blackbox->getLowerRightAngleCursor()); positionWindows(); decorate(); if (decorations & Decor_Titlebar) XMapSubwindows(blackbox->getXDisplay(), frame.title); XMapSubwindows(blackbox->getXDisplay(), frame.window); windowmenu = new Windowmenu(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()); } // preserve the window's initial state on first map, and its current state // across a restart if (! getState()) { if (client.wm_hint_flags & StateHint) current_state = client.initial_state; else current_state = NormalState; } if (flags.shaded) { flags.shaded = False; shade(); /* Because the iconic'ness of shaded windows is lost, we need to set the state to NormalState so that shaded windows on other workspaces will not get shown on the first workspace. 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. */ current_state = NormalState; } if (flags.stuck) { flags.stuck = False; stick(); } if (flags.maximized && (functions & Func_Maximize)) { remaximize(); } /* When the window is mapped (and also when its attributes are restored), the current_state that was set here will be used. It is set to Normal if the window is to be mapped or it is set to Iconic if the window is to be iconified. *Note* that for sticky windows, the same rules apply here, they are in fact never set to Iconic since there is no way for us to tell if a sticky window was iconified previously. */ setFocusFlag(False); } 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 || flags.resizing) { screen->hideGeometry(); XUngrabPointer(blackbox->getXDisplay(), CurrentTime); } delete timer; delete windowmenu; 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(blackbox->getXDisplay(), frame.plate); } if (frame.window) { blackbox->removeWindowSearch(frame.window); XDestroyWindow(blackbox->getXDisplay(), frame.window); } blackbox->removeWindowSearch(client.window); } /* * 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 = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask; return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(), -1, -1, 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, Cursor cursor) { XSetWindowAttributes attrib_create; unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask; attrib_create.background_pixmap = None; attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask; if (cursor) { create_mask |= CWCursor; attrib_create.cursor = cursor; } return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0, screen->getDepth(), InputOutput, screen->getVisual(), create_mask, &attrib_create); } void BlackboxWindow::associateClientWindow(void) { XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0); getWMName(); getWMIconName(); XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert); XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask); XGrabServer(blackbox->getXDisplay()); XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask); XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0); XSelectInput(blackbox->getXDisplay(), client.window, PropertyChangeMask | FocusChangeMask | StructureNotifyMask); XUngrabServer(blackbox->getXDisplay()); XRaiseWindow(blackbox->getXDisplay(), frame.plate); XMapSubwindows(blackbox->getXDisplay(), frame.plate); #ifdef SHAPE if (blackbox->hasShapeExtensions()) { XShapeSelectInput(blackbox->getXDisplay(), client.window, ShapeNotifyMask); Bool shaped = False; int foo; unsigned int ufoo; XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped, &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo); flags.shaped = shaped; } #endif // SHAPE } void BlackboxWindow::decorate(void) { 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(); texture = &(screen->getWindowStyle()->b_pressed); frame.pbutton = texture->render(frame.button_w, frame.button_w, frame.pbutton); if (! frame.pbutton) frame.pbutton_pixel = 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(blackbox->getXDisplay(), frame.title, screen->getBorderColor()->pixel()); decorateLabel(); } if (decorations & Decor_Border) { frame.fborder_pixel = screen->getWindowStyle()->f_focus.pixel(); frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.pixel(); blackbox_attrib.flags |= AttribDecoration; blackbox_attrib.decoration = DecorNormal; } else { blackbox_attrib.flags |= AttribDecoration; blackbox_attrib.decoration = DecorNone; } 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(blackbox->getXDisplay(), frame.handle, screen->getBorderColor()->pixel()); XSetWindowBorder(blackbox->getXDisplay(), frame.left_grip, screen->getBorderColor()->pixel()); XSetWindowBorder(blackbox->getXDisplay(), frame.right_grip, screen->getBorderColor()->pixel()); } XSetWindowBorder(blackbox->getXDisplay(), frame.window, screen->getBorderColor()->pixel()); } void BlackboxWindow::decorateLabel(void) { 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); blackbox->saveWindowSearch(frame.handle, this); frame.left_grip = createChildWindow(frame.handle, blackbox->getLowerLeftAngleCursor()); blackbox->saveWindowSearch(frame.left_grip, this); frame.right_grip = createChildWindow(frame.handle, 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(blackbox->getXDisplay(), frame.left_grip); XDestroyWindow(blackbox->getXDisplay(), frame.right_grip); frame.left_grip = frame.right_grip = None; blackbox->removeWindowSearch(frame.handle); XDestroyWindow(blackbox->getXDisplay(), frame.handle); frame.handle = None; } void BlackboxWindow::createTitlebar(void) { frame.title = createChildWindow(frame.window); frame.label = createChildWindow(frame.title); 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.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); if (frame.pbutton) screen->getImageControl()->removeImage(frame.pbutton); blackbox->removeWindowSearch(frame.title); blackbox->removeWindowSearch(frame.label); XDestroyWindow(blackbox->getXDisplay(), frame.label); XDestroyWindow(blackbox->getXDisplay(), frame.title); frame.title = frame.label = None; } void BlackboxWindow::createCloseButton(void) { if (frame.title != None) { frame.close_button = createChildWindow(frame.title); blackbox->saveWindowSearch(frame.close_button, this); } } void BlackboxWindow::destroyCloseButton(void) { blackbox->removeWindowSearch(frame.close_button); XDestroyWindow(blackbox->getXDisplay(), frame.close_button); frame.close_button = None; } void BlackboxWindow::createIconifyButton(void) { if (frame.title != None) { frame.iconify_button = createChildWindow(frame.title); blackbox->saveWindowSearch(frame.iconify_button, this); } } void BlackboxWindow::destroyIconifyButton(void) { blackbox->removeWindowSearch(frame.iconify_button); XDestroyWindow(blackbox->getXDisplay(), frame.iconify_button); frame.iconify_button = None; } void BlackboxWindow::createMaximizeButton(void) { if (frame.title != None) { frame.maximize_button = createChildWindow(frame.title); blackbox->saveWindowSearch(frame.maximize_button, this); } } void BlackboxWindow::destroyMaximizeButton(void) { blackbox->removeWindowSearch(frame.maximize_button); XDestroyWindow(blackbox->getXDisplay(), frame.maximize_button); frame.maximize_button = None; } void BlackboxWindow::positionButtons(bool redecorate_label) { string layout = blackbox->getTitlebarLayout(); string parsed; bool hasclose, hasiconify, hasmaximize, haslabel; hasclose = hasiconify = hasmaximize = haslabel = 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 'M': if (! hasmaximize && (decorations & Decor_Maximize)) { hasmaximize = true; parsed += *it; } break; case 'L': if (! haslabel) { haslabel = true; parsed += *it; } } } if (! hasclose && frame.close_button) destroyCloseButton(); if (! hasiconify && frame.iconify_button) destroyIconifyButton(); if (! hasmaximize && frame.maximize_button) destroyMaximizeButton(); 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(blackbox->getXDisplay(), 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(blackbox->getXDisplay(), frame.iconify_button, x, by, frame.button_w, frame.button_w); x += frame.button_w + bsep; break; case 'M': if (!frame.maximize_button) createMaximizeButton(); XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by, frame.button_w, frame.button_w); x += frame.button_w + bsep; break; case 'L': XMoveResizeWindow(blackbox->getXDisplay(), 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) { upsize(); client.rect.setPos(frame.rect.left() + frame.margin.left, frame.rect.top() + frame.margin.top); positionWindows(); decorate(); XClearWindow(blackbox->getXDisplay(), frame.window); setFocusFlag(flags.focused); configure(frame.rect.x(), frame.rect.y(), frame.rect.width(), frame.rect.height()); if (windowmenu) { windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h); windowmenu->reconfigure(); } } void BlackboxWindow::updateFocusModel(void) { if ((! screen->isSloppyFocus()) || screen->doClickRaise()) { // grab button 1 for changing focus/raising blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None); } else { blackbox->ungrabButton(Button1, 0, frame.plate); } } void BlackboxWindow::positionWindows(void) { XMoveResizeWindow(blackbox->getXDisplay(), frame.window, frame.rect.x(), frame.rect.y(), frame.inside_w, (flags.shaded) ? frame.title_h : frame.inside_h); XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window, frame.border_w); XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate, frame.mwm_border_w); XMoveResizeWindow(blackbox->getXDisplay(), 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(blackbox->getXDisplay(), client.window, 0, 0, client.rect.width(), client.rect.height()); if (decorations & Decor_Titlebar) { if (frame.title == None) createTitlebar(); XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title, frame.border_w); XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w, -frame.border_w, frame.inside_w, frame.title_h); positionButtons(); XMapSubwindows(blackbox->getXDisplay(), frame.title); XMapWindow(blackbox->getXDisplay(), frame.title); } else if (frame.title) { destroyTitlebar(); } if (decorations & Decor_Handle) { if (frame.handle == None) createHandle(); XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle, frame.border_w); XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip, frame.border_w); XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip, frame.border_w); // use client.rect here so the value is correct even if shaded XMoveResizeWindow(blackbox->getXDisplay(), 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(blackbox->getXDisplay(), frame.left_grip, -frame.border_w, -frame.border_w, frame.grip_w, frame.handle_h); XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip, frame.inside_w - frame.grip_w - frame.border_w, -frame.border_w, frame.grip_w, frame.handle_h); XMapSubwindows(blackbox->getXDisplay(), frame.handle); XMapWindow(blackbox->getXDisplay(), frame.handle); } else if (frame.handle) { destroyHandle(); } } void BlackboxWindow::getWMName(void) { XTextProperty text_prop; if (XGetWMName(blackbox->getXDisplay(), client.window, &text_prop)) { client.title = textPropertyToString(blackbox->getXDisplay(), text_prop); if (client.title.empty()) client.title = i18n(WindowSet, WindowUnnamed, "Unnamed"); XFree((char *) text_prop.value); } else { client.title = i18n(WindowSet, WindowUnnamed, "Unnamed"); } } void BlackboxWindow::getWMIconName(void) { XTextProperty text_prop; if (XGetWMIconName(blackbox->getXDisplay(), client.window, &text_prop)) { client.icon_title = textPropertyToString(blackbox->getXDisplay(), text_prop); if (client.icon_title.empty()) client.icon_title = client.title; XFree((char *) text_prop.value); } else { client.icon_title = client.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(blackbox->getXDisplay(), client.window, &proto, &num_return)) { for (int i = 0; i < num_return; ++i) { if (proto[i] == blackbox->getWMDeleteAtom()) { decorations |= Decor_Close; functions |= Func_Close; } else if (proto[i] == blackbox->getWMTakeFocusAtom()) flags.send_focus_message = True; else if (proto[i] == blackbox->getBlackboxStructureMessagesAtom()) screen->addNetizen(new Netizen(screen, client.window)); } 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; client.initial_state = NormalState; // 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(blackbox->getXDisplay(), 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) client.initial_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! group = new BWindowGroup(blackbox, client.window_group); group->addWindow(this); } client.wm_hint_flags = wmhint->flags; 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; /* use the full screen, not the strut modified size. otherwise when the availableArea changes max_width/height will be incorrect and lead to odd rendering bugs. */ const Rect& screen_area = screen->getRect(); client.max_width = screen_area.width(); client.max_height = screen_area.height(); client.min_aspect_x = client.min_aspect_y = client.max_aspect_x = client.max_aspect_y = 1; client.win_gravity = NorthWestGravity; if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window, &sizehint, &icccm_mask)) return; client.normal_hint_flags = sizehint.flags; if (sizehint.flags & PMinSize) { client.min_width = sizehint.min_width; client.min_height = sizehint.min_height; } if (sizehint.flags & PMaxSize) { client.max_width = sizehint.max_width; client.max_height = sizehint.max_height; } if (sizehint.flags & PResizeInc) { client.width_inc = sizehint.width_inc; client.height_inc = sizehint.height_inc; } 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; } 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 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) { int format; Atom atom_return; unsigned long num, len; MwmHints *mwm_hint = 0; int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window, blackbox->getMotifWMHintsAtom(), 0, PropMwmHintsElements, False, blackbox->getMotifWMHintsAtom(), &atom_return, &format, &num, &len, (unsigned char **) &mwm_hint); if (ret != Success || ! mwm_hint || num != PropMwmHintsElements) return; if (mwm_hint->flags & MwmHintsDecorations) { if (mwm_hint->decorations & MwmDecorAll) { decorations = Decor_Titlebar | Decor_Handle | Decor_Border | Decor_Iconify | Decor_Maximize | Decor_Close; } else { decorations = 0; if (mwm_hint->decorations & MwmDecorBorder) decorations |= Decor_Border; if (mwm_hint->decorations & MwmDecorHandle) decorations |= Decor_Handle; if (mwm_hint->decorations & MwmDecorTitle) decorations |= Decor_Titlebar; if (mwm_hint->decorations & MwmDecorIconify) decorations |= Decor_Iconify; if (mwm_hint->decorations & MwmDecorMaximize) 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; } } XFree(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) { int format; Atom atom_return; unsigned long num, len; BlackboxHints *blackbox_hint = 0; int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window, blackbox->getBlackboxHintsAtom(), 0, PropBlackboxHintsElements, False, blackbox->getBlackboxHintsAtom(), &atom_return, &format, &num, &len, (unsigned char **) &blackbox_hint); if (ret != Success || ! blackbox_hint || num != PropBlackboxHintsElements) 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: // clear all decorations except close decorations &= Decor_Close; // clear all functions except close functions &= Func_Close; break; case DecorTiny: decorations |= Decor_Titlebar | Decor_Iconify; decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize); functions |= Func_Move | Func_Iconify; functions &= ~(Func_Resize | Func_Maximize); break; case DecorTool: decorations |= Decor_Titlebar; decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle); functions |= Func_Move; functions &= ~(Func_Resize | Func_Maximize | Func_Iconify); break; case DecorNormal: default: decorations |= Decor_Titlebar | Decor_Border | Decor_Handle | Decor_Iconify | Decor_Maximize; functions |= Func_Resize | Func_Move | Func_Iconify | Func_Maximize; break; } reconfigure(); } XFree(blackbox_hint); return True; } void BlackboxWindow::getTransientInfo(void) { if (client.transient_for && client.transient_for != (BlackboxWindow *) ~0ul) { // the transient for hint was removed, so we need to tell our // previous transient_for that we are going away client.transient_for->client.transientList.remove(this); } // we have no transient_for until we find a new one client.transient_for = 0; Window trans_for; if (! XGetTransientForHint(blackbox->getXDisplay(), 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; } // 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; } void BlackboxWindow::configure(int dx, int dy, unsigned int dw, unsigned int dh) { bool send_event = (frame.rect.x() != dx || frame.rect.y() != dy); 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(); setFocusFlag(flags.focused); redrawAllButtons(); } else { frame.rect.setPos(dx, dy); XMoveWindow(blackbox->getXDisplay(), frame.window, frame.rect.x(), frame.rect.y()); if (! flags.moving) send_event = True; } if (send_event && ! flags.moving) { client.rect.setPos(frame.rect.left() + frame.margin.left, frame.rect.top() + frame.margin.top); XEvent event; event.type = ConfigureNotify; event.xconfigure.display = blackbox->getXDisplay(); 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(blackbox->getXDisplay(), client.window, True, NoEventMask, &event); screen->updateNetizenConfigNotify(&event); } } #ifdef SHAPE void BlackboxWindow::configureShape(void) { XShapeCombineShape(blackbox->getXDisplay(), 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(blackbox->getXDisplay(), frame.window, ShapeBounding, 0, 0, xrect, num, ShapeUnion, Unsorted); } #endif // SHAPE bool BlackboxWindow::setInputFocus(void) { if (flags.focused) return True; assert(! flags.iconic); // if the window is not visible, mark the window as wanting focus rather // than give it focus. if (! flags.visible) { Workspace *wkspc = screen->getWorkspace(blackbox_attrib.workspace); wkspc->setLastFocusedWindow(this); return True; } if (! client.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(blackbox->getXDisplay(), client.window, RevertToPointerRoot, CurrentTime); blackbox->setFocusedWindow(this); } 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 = blackbox->getWMProtocolsAtom(); ce.xclient.display = blackbox->getXDisplay(); ce.xclient.window = client.window; ce.xclient.format = 32; ce.xclient.data.l[0] = blackbox->getWMTakeFocusAtom(); 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(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce); } return ret; } void BlackboxWindow::iconify(void) { if (flags.iconic) return; if (windowmenu) windowmenu->hide(); setState(IconicState); /* * 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 */ XGrabServer(blackbox->getXDisplay()); XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask); XUnmapWindow(blackbox->getXDisplay(), client.window); XSelectInput(blackbox->getXDisplay(), client.window, PropertyChangeMask | FocusChangeMask | StructureNotifyMask); XUngrabServer(blackbox->getXDisplay()); XUnmapWindow(blackbox->getXDisplay(), frame.window); flags.visible = False; flags.iconic = True; screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this); 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(); } } } void BlackboxWindow::show(void) { current_state = (flags.shaded) ? IconicState : NormalState; setState(current_state); XMapWindow(blackbox->getXDisplay(), client.window); XMapSubwindows(blackbox->getXDisplay(), frame.window); XMapWindow(blackbox->getXDisplay(), frame.window); flags.visible = True; flags.iconic = False; } void BlackboxWindow::deiconify(bool reassoc, bool raise) { if (flags.iconic || reassoc) screen->reassociateWindow(this, BSENTINEL, False); else if (blackbox_attrib.workspace != screen->getCurrentWorkspace()->getID()) 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) { XEvent ce; ce.xclient.type = ClientMessage; ce.xclient.message_type = blackbox->getWMProtocolsAtom(); ce.xclient.display = blackbox->getXDisplay(); ce.xclient.window = client.window; ce.xclient.format = 32; ce.xclient.data.l[0] = blackbox->getWMDeleteAtom(); 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(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce); } void BlackboxWindow::withdraw(void) { setState(current_state); flags.visible = False; flags.iconic = False; XUnmapWindow(blackbox->getXDisplay(), frame.window); XGrabServer(blackbox->getXDisplay()); XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask); XUnmapWindow(blackbox->getXDisplay(), client.window); XSelectInput(blackbox->getXDisplay(), client.window, PropertyChangeMask | FocusChangeMask | StructureNotifyMask); XUngrabServer(blackbox->getXDisplay()); if (windowmenu) windowmenu->hide(); } void BlackboxWindow::maximize(unsigned int button) { // handle case where menu is open then the max button is used instead if (windowmenu && windowmenu->isVisible()) windowmenu->hide(); if (flags.maximized) { flags.maximized = 0; blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert); blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert); /* when a resize is begun, 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(); 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; const Rect &screen_area = screen->availableArea(); frame.changing = screen_area; constrain(TopLeft); 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; } 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(); setState(current_state); } // re-maximizes the window to take into account availableArea changes void BlackboxWindow::remaximize(void) { // 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; } void BlackboxWindow::shade(void) { if (flags.shaded) { XResizeWindow(blackbox->getXDisplay(), 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; XResizeWindow(blackbox->getXDisplay(), 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)); } } void BlackboxWindow::stick(void) { if (flags.stuck) { blackbox_attrib.flags ^= AttribOmnipresent; blackbox_attrib.attrib ^= AttribOmnipresent; flags.stuck = False; if (! flags.iconic) screen->reassociateWindow(this, BSENTINEL, True); setState(current_state); } else { flags.stuck = True; blackbox_attrib.flags |= AttribOmnipresent; blackbox_attrib.attrib |= AttribOmnipresent; setState(current_state); } } void BlackboxWindow::setFocusFlag(bool focus) { // only focus a window if it is visible if (focus && !flags.visible) return; flags.focused = focus; if (decorations & Decor_Titlebar) { if (flags.focused) { if (frame.ftitle) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.title, frame.ftitle); else XSetWindowBackground(blackbox->getXDisplay(), frame.title, frame.ftitle_pixel); } else { if (frame.utitle) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.title, frame.utitle); else XSetWindowBackground(blackbox->getXDisplay(), frame.title, frame.utitle_pixel); } XClearWindow(blackbox->getXDisplay(), frame.title); redrawLabel(); redrawAllButtons(); } if (decorations & Decor_Handle) { if (flags.focused) { if (frame.fhandle) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.handle, frame.fhandle); else XSetWindowBackground(blackbox->getXDisplay(), frame.handle, frame.fhandle_pixel); if (frame.fgrip) { XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.left_grip, frame.fgrip); XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.right_grip, frame.fgrip); } else { XSetWindowBackground(blackbox->getXDisplay(), frame.left_grip, frame.fgrip_pixel); XSetWindowBackground(blackbox->getXDisplay(), frame.right_grip, frame.fgrip_pixel); } } else { if (frame.uhandle) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.handle, frame.uhandle); else XSetWindowBackground(blackbox->getXDisplay(), frame.handle, frame.uhandle_pixel); if (frame.ugrip) { XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.left_grip, frame.ugrip); XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.right_grip, frame.ugrip); } else { XSetWindowBackground(blackbox->getXDisplay(), frame.left_grip, frame.ugrip_pixel); XSetWindowBackground(blackbox->getXDisplay(), frame.right_grip, frame.ugrip_pixel); } } XClearWindow(blackbox->getXDisplay(), frame.handle); XClearWindow(blackbox->getXDisplay(), frame.left_grip); XClearWindow(blackbox->getXDisplay(), frame.right_grip); } if (decorations & Decor_Border) { if (flags.focused) XSetWindowBorder(blackbox->getXDisplay(), frame.plate, frame.fborder_pixel); else XSetWindowBorder(blackbox->getXDisplay(), frame.plate, frame.uborder_pixel); } if (screen->isSloppyFocus() && screen->doAutoRaise()) { if (isFocused()) timer->start(); else timer->stop(); } if (isFocused()) blackbox->setFocusedWindow(this); } void BlackboxWindow::installColormap(bool install) { int i = 0, ncmap = 0; Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(), client.window, &ncmap); XWindowAttributes wattrib; if (cmaps) { if (XGetWindowAttributes(blackbox->getXDisplay(), 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(blackbox->getXDisplay(), 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(blackbox->getXDisplay(), wattrib.colormap); } } } XFree(cmaps); } } 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); } bool BlackboxWindow::getState(void) { current_state = 0; Atom atom_return; bool ret = False; unsigned long *state, nitems; if (! xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state, nitems, &state)) return False; current_state = static_cast(state[0]); delete state; return True; } void BlackboxWindow::restoreAttributes(void) { Atom atom_return; int foo; unsigned long ulfoo, nitems; BlackboxAttributes *net; int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window, blackbox->getBlackboxAttributesAtom(), 0l, PropBlackboxAttributesElements, False, blackbox->getBlackboxAttributesAtom(), &atom_return, &foo, &nitems, &ulfoo, (unsigned char **) &net); if (ret != Success || ! net || nitems != PropBlackboxAttributesElements) return; if (net->flags & AttribShaded && net->attrib & AttribShaded) { flags.shaded = False; shade(); /* Because the iconic'ness of shaded windows is lost, we need to set the state to NormalState so that shaded windows on other workspaces will not get shown on the first workspace. 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. */ current_state = NormalState; } if ((net->workspace != screen->getCurrentWorkspaceID()) && (net->workspace < screen->getWorkspaceCount())) { screen->reassociateWindow(this, net->workspace, True); // 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 = False; 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; } // with the state set it will then be the map events job to read the window's // state and behave accordingly XFree((void *) net); } /* * Positions the frame according the the client window position and window * gravity. */ void BlackboxWindow::setGravityOffsets(void) { // x coordinates for each gravity type const int x_west = client.rect.x(); const int x_east = client.rect.right() - frame.inside_w + 1; const int x_center = client.rect.left() + ((client.rect.width() - frame.rect.width()) / 2); // y coordinates for each gravity type const int y_north = client.rect.y(); const int y_south = client.rect.bottom() - frame.inside_h + 1; const int y_center = client.rect.top() + ((client.rect.height() - frame.rect.height()) / 2); switch (client.win_gravity) { default: case NorthWestGravity: frame.rect.setPos(x_west, y_north); break; case NorthGravity: frame.rect.setPos(x_center, y_north); break; case NorthEastGravity: frame.rect.setPos(x_east, y_north); break; case SouthWestGravity: frame.rect.setPos(x_west, y_south); break; case SouthGravity: frame.rect.setPos(x_center, y_south); break; case SouthEastGravity: frame.rect.setPos(x_east, y_south); break; case WestGravity: frame.rect.setPos(x_west, y_center); break; case CenterGravity: frame.rect.setPos(x_center, y_center); break; case EastGravity: frame.rect.setPos(x_east, y_center); break; case ForgetGravity: case StaticGravity: frame.rect.setPos(client.rect.x() - frame.margin.left, client.rect.y() - frame.margin.top); break; } } /* * The reverse of the setGravityOffsets function. Uses the frame window's * position to find the window's reference point. */ void BlackboxWindow::restoreGravity(void) { // x coordinates for each gravity type const int x_west = frame.rect.x(); const int x_east = frame.rect.x() + frame.inside_w - client.rect.width(); const int x_center = frame.rect.x() - ((client.rect.width() - frame.rect.width()) / 2); // y coordinates for each gravity type const int y_north = frame.rect.y(); const int y_south = frame.rect.y() + frame.inside_h - client.rect.height(); const int y_center = frame.rect.y() - ((client.rect.height() - frame.rect.height()) / 2); switch(client.win_gravity) { default: case NorthWestGravity: client.rect.setPos(x_west, y_north); break; case NorthGravity: client.rect.setPos(x_center, y_north); break; case NorthEastGravity: client.rect.setPos(x_east, y_north); break; case SouthWestGravity: client.rect.setPos(x_west, y_south); break; case SouthGravity: client.rect.setPos(x_center, y_south); break; case SouthEastGravity: client.rect.setPos(x_east, y_south); break; case WestGravity: client.rect.setPos(x_west, y_center); break; case CenterGravity: client.rect.setPos(x_center, y_center); break; case EastGravity: client.rect.setPos(x_east, y_center); break; case ForgetGravity: case StaticGravity: client.rect.setPos(frame.rect.left() + frame.margin.left, frame.rect.top() + frame.margin.top); break; } } void BlackboxWindow::redrawLabel(void) { if (flags.focused) { if (frame.flabel) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.label, frame.flabel); else XSetWindowBackground(blackbox->getXDisplay(), frame.label, frame.flabel_pixel); } else { if (frame.ulabel) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.label, frame.ulabel); else XSetWindowBackground(blackbox->getXDisplay(), frame.label, frame.ulabel_pixel); } XClearWindow(blackbox->getXDisplay(), frame.label); WindowStyle *style = screen->getWindowStyle(); int pos = frame.bevel_w * 2, dlen = style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4, i18n.multibyte()); BPen pen((flags.focused) ? style->l_text_focus : style->l_text_unfocus, style->font); if (i18n.multibyte()) XmbDrawString(blackbox->getXDisplay(), frame.label, style->fontset, pen.gc(), pos, (1 - style->fontset_extents->max_ink_extent.y), client.title.c_str(), dlen); else XDrawString(blackbox->getXDisplay(), frame.label, pen.gc(), pos, (style->font->ascent + 1), client.title.c_str(), dlen); } void BlackboxWindow::redrawAllButtons(void) { if (frame.iconify_button) redrawIconifyButton(False); if (frame.maximize_button) redrawMaximizeButton(flags.maximized); if (frame.close_button) redrawCloseButton(False); } void BlackboxWindow::redrawIconifyButton(bool pressed) { if (! pressed) { if (flags.focused) { if (frame.fbutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.iconify_button, frame.fbutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button, frame.fbutton_pixel); } else { if (frame.ubutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.iconify_button, frame.ubutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button, frame.ubutton_pixel); } } else { if (frame.pbutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.iconify_button, frame.pbutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button, frame.pbutton_pixel); } XClearWindow(blackbox->getXDisplay(), frame.iconify_button); BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : screen->getWindowStyle()->b_pic_unfocus); XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(), 2, (frame.button_w - 5), (frame.button_w - 5), 2); } void BlackboxWindow::redrawMaximizeButton(bool pressed) { if (! pressed) { if (flags.focused) { if (frame.fbutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.maximize_button, frame.fbutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button, frame.fbutton_pixel); } else { if (frame.ubutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.maximize_button, frame.ubutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button, frame.ubutton_pixel); } } else { if (frame.pbutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.maximize_button, frame.pbutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button, frame.pbutton_pixel); } XClearWindow(blackbox->getXDisplay(), frame.maximize_button); BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : screen->getWindowStyle()->b_pic_unfocus); XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(), 2, 2, (frame.button_w - 5), (frame.button_w - 5)); XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(), 2, 3, (frame.button_w - 3), 3); } void BlackboxWindow::redrawCloseButton(bool pressed) { if (! pressed) { if (flags.focused) { if (frame.fbutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button, frame.fbutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.close_button, frame.fbutton_pixel); } else { if (frame.ubutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button, frame.ubutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.close_button, frame.ubutton_pixel); } } else { if (frame.pbutton) XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button, frame.pbutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.close_button, frame.pbutton_pixel); } XClearWindow(blackbox->getXDisplay(), frame.close_button); BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : screen->getWindowStyle()->b_pic_unfocus); XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(), 2, 2, (frame.button_w - 3), (frame.button_w - 3)); XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(), 2, (frame.button_w - 3), (frame.button_w - 3), 2); } void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) { if (re->window != client.window) return; #ifdef DEBUG fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n", client.window); #endif // DEBUG 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 (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) { XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped.. setInputFocus(); } break; } } void BlackboxWindow::unmapNotifyEvent(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(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(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(blackbox->getXDisplay(), &ev); screen->unmanageWindow(this, True); } void BlackboxWindow::propertyNotifyEvent(Atom atom) { switch(atom) { case XA_WM_CLASS: case XA_WM_CLIENT_MACHINE: case XA_WM_COMMAND: break; case XA_WM_TRANSIENT_FOR: { // determine if this is a transient window getTransientInfo(); // adjust the window decorations based on transience if (isTransient()) { decorations &= ~(Decor_Maximize | Decor_Handle); functions &= ~Func_Maximize; } reconfigure(); } break; case XA_WM_HINTS: getWMHints(); break; case XA_WM_ICON_NAME: getWMIconName(); if (flags.iconic) screen->propagateWindowName(this); break; 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)) { if (client.max_width <= client.min_width && client.max_height <= client.min_height) { decorations &= ~(Decor_Maximize | Decor_Handle); functions &= ~(Func_Resize | Func_Maximize); } else { decorations |= Decor_Maximize | Decor_Handle; functions |= Func_Resize | Func_Maximize; } } Rect old_rect = frame.rect; upsize(); if (old_rect != frame.rect) reconfigure(); break; } default: if (atom == blackbox->getWMProtocolsAtom()) { getWMProtocols(); if ((decorations & Decor_Close) && (! frame.close_button)) { createCloseButton(); if (decorations & Decor_Titlebar) { positionButtons(True); XMapSubwindows(blackbox->getXDisplay(), frame.title); } if (windowmenu) windowmenu->reconfigure(); } } break; } } void BlackboxWindow::exposeEvent(XExposeEvent *ee) { 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); } void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) { if (cr->window != client.window || flags.iconic) return; int cx = frame.rect.x(), cy = frame.rect.y(); unsigned int cw = frame.rect.width(), ch = frame.rect.height(); if (cr->value_mask & CWBorderWidth) client.old_bw = cr->border_width; if (cr->value_mask & CWX) cx = cr->x - frame.margin.left; if (cr->value_mask & CWY) cy = cr->y - frame.margin.top; if (cr->value_mask & CWWidth) cw = cr->width + frame.margin.left + frame.margin.right; if (cr->value_mask & CWHeight) ch = cr->height + frame.margin.top + frame.margin.bottom; if (frame.rect != Rect(cx, cy, cw, ch)) configure(cx, cy, cw, ch); if (cr->value_mask & CWStackMode) { 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(XButtonEvent *be) { if (frame.maximize_button == be->window) { redrawMaximizeButton(True); } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) { if (! flags.focused) setInputFocus(); if (frame.iconify_button == be->window) { redrawIconifyButton(True); } else if (frame.close_button == be->window) { redrawCloseButton(True); } else if (frame.plate == be->window) { if (windowmenu && windowmenu->isVisible()) windowmenu->hide(); screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); XAllowEvents(blackbox->getXDisplay(), 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; } } frame.grab_x = be->x_root - frame.rect.x() - frame.border_w; frame.grab_y = be->y_root - frame.rect.y() - frame.border_w; if (windowmenu && windowmenu->isVisible()) windowmenu->hide(); screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); } } else if (be->button == 2 && (be->window != frame.iconify_button) && (be->window != frame.close_button)) { screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); } else if (windowmenu && be->button == 3 && (frame.title == be->window || frame.label == be->window || frame.handle == be->window || frame.window == be->window)) { int mx = 0, my = 0; if (frame.title == be->window || frame.label == be->window) { mx = be->x_root - (windowmenu->getWidth() / 2); my = frame.rect.y() + frame.title_h + frame.border_w; } else if (frame.handle == be->window) { mx = be->x_root - (windowmenu->getWidth() / 2); my = frame.rect.bottom() - frame.handle_h - (frame.border_w * 3) - windowmenu->getHeight(); } else { mx = be->x_root - (windowmenu->getWidth() / 2); if (be->y <= static_cast(frame.bevel_w)) my = frame.rect.y() + frame.title_h; else my = be->y_root - (windowmenu->getHeight() / 2); } // snap the window menu into a corner if necessary - we check the // position of the menu with the coordinates of the client to // make the comparisions easier. // XXX: this needs some work! if (mx > client.rect.right() - static_cast(windowmenu->getWidth())) mx = frame.rect.right() - windowmenu->getWidth() - frame.border_w + 1; if (mx < client.rect.left()) mx = frame.rect.x(); if (my > client.rect.bottom() - static_cast(windowmenu->getHeight())) my = frame.rect.bottom() - windowmenu->getHeight() - frame.border_w + 1; if (my < client.rect.top()) my = frame.rect.y() + ((decorations & Decor_Titlebar) ? frame.title_h : 0); if (windowmenu) { if (! windowmenu->isVisible()) { windowmenu->move(mx, my); windowmenu->show(); XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID()); XRaiseWindow(blackbox->getXDisplay(), windowmenu->getSendToMenu()->getWindowID()); } else { windowmenu->hide(); } } // mouse wheel up } else if (be->button == 4) { if ((be->window == frame.label || be->window == frame.title) && ! flags.shaded) shade(); // mouse wheel down } else if (be->button == 5) { if ((be->window == frame.label || be->window == frame.title) && flags.shaded) shade(); } } void BlackboxWindow::buttonReleaseEvent(XButtonEvent *re) { if (re->window == frame.maximize_button) { 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) { 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.close_button) { 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) { flags.moving = False; 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(blackbox->getXDisplay(), screen->getRootWindow(), screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); XUngrabServer(blackbox->getXDisplay()); 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(blackbox->getXDisplay(), CurrentTime); } else if (flags.resizing) { XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); XUngrabServer(blackbox->getXDisplay()); screen->hideGeometry(); constrain((re->window == frame.left_grip) ? TopRight : TopLeft); // unset maximized state when resized after fully maximized if (flags.maximized == 1) maximize(0); flags.resizing = False; configure(frame.changing.x(), frame.changing.y(), frame.changing.width(), frame.changing.height()); XUngrabPointer(blackbox->getXDisplay(), CurrentTime); } else if (re->window == frame.window) { if (re->button == 2 && re->state == Mod1Mask) XUngrabPointer(blackbox->getXDisplay(), CurrentTime); } } void BlackboxWindow::motionNotifyEvent(XMotionEvent *me) { if (! flags.resizing && (me->state & Button1Mask) && (functions & Func_Move) && (frame.title == me->window || frame.label == me->window || frame.handle == me->window || frame.window == me->window)) { if (! flags.moving) { XGrabPointer(blackbox->getXDisplay(), me->window, False, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, blackbox->getMoveCursor(), CurrentTime); if (windowmenu && windowmenu->isVisible()) windowmenu->hide(); flags.moving = True; if (! screen->doOpaqueMove()) { XGrabServer(blackbox->getXDisplay()); frame.changing = frame.rect; screen->showPosition(frame.changing.x(), frame.changing.y()); XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); } } else { int dx = me->x_root - frame.grab_x, dy = me->y_root - frame.grab_y; dx -= frame.border_w; dy -= frame.border_w; const int snap_distance = screen->getEdgeSnapThreshold(); if (snap_distance) { // window corners const int wleft = dx, wright = dx + frame.rect.width() - 1, wtop = dy, wbottom = dy + frame.rect.height() - 1; // Maybe this should be saved in the class, and set in the setWorkspace // function!! Workspace *w = screen->getWorkspace(getWorkspaceNumber()); assert(w); if (screen->getWindowToWindowSnap()) { // try snap to another window for (unsigned int i = 0, c = w->getCount(); i < c; ++i) { BlackboxWindow *snapwin = w->getWindow(i); if (snapwin == this) continue; // don't snap to self const Rect &winrect = snapwin->frameRect(); int dleft = std::abs(wright - winrect.left()), dright = std::abs(wleft - winrect.right()), dtop = std::abs(wbottom - winrect.top()), dbottom = std::abs(wtop - winrect.bottom()); // snap top of other window? if (dtop < snap_distance && dtop <= dbottom) { dy = winrect.top() - frame.rect.height(); if (screen->getWindowCornerSnap()) { // try corner-snap to its other sides dleft = std::abs(wleft - winrect.left()); dright = std::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; } // snap bottom of other window? else if (dbottom < snap_distance) { dy = winrect.bottom() + 1; if (screen->getWindowCornerSnap()) { // try corner-snap to its other sides dleft = std::abs(wleft - winrect.left()); dright = std::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; } // snap left of other window? if (dleft < snap_distance && dleft <= dright) { dx = winrect.left() - frame.rect.width(); if (screen->getWindowCornerSnap()) { // try corner-snap to its other sides dtop = std::abs(wtop - winrect.top()); dbottom = std::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; } // snap right of other window? else if (dright < snap_distance) { dx = winrect.right() + 1; if (screen->getWindowCornerSnap()) { // try corner-snap to its other sides dtop = std::abs(wtop - winrect.top()); dbottom = std::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; } } } // try snap to the screen's available area Rect srect = screen->availableArea(); int dleft = std::abs(wleft - srect.left()), dright = std::abs(wright - srect.right()), dtop = std::abs(wtop - srect.top()), dbottom = std::abs(wbottom - srect.bottom()); // snap left? if (dleft < snap_distance && dleft <= dright) dx = srect.left(); // snap right? else if (dright < snap_distance) dx = srect.right() - frame.rect.width() + 1; // snap top? if (dtop < snap_distance && dtop <= dbottom) dy = srect.top(); // snap bottom? else if (dbottom < snap_distance) dy = srect.bottom() - frame.rect.height() + 1; if (! screen->doFullMax()) { srect = screen->getRect(); // now get the full screen dleft = std::abs(wleft - srect.left()), dright = std::abs(wright - srect.right()), dtop = std::abs(wtop - srect.top()), dbottom = std::abs(wbottom - srect.bottom()); // snap left? if (dleft < snap_distance && dleft <= dright) dx = srect.left(); // snap right? else if (dright < snap_distance) dx = srect.right() - frame.rect.width() + 1; // snap top? if (dtop < snap_distance && dtop <= dbottom) dy = srect.top(); // snap bottom? else if (dbottom < snap_distance) dy = srect.bottom() - frame.rect.height() + 1; } } if (screen->doOpaqueMove()) { configure(dx, dy, frame.rect.width(), frame.rect.height()); } else { XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); frame.changing.setPos(dx, dy); XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); } screen->showPosition(dx, dy); } } else if ((functions & Func_Resize) && (((me->state & Button1Mask) && (me->window == frame.right_grip || me->window == frame.left_grip)) || (me->state & (Mod1Mask | Button3Mask) && me->window == frame.window))) { bool left = (me->window == frame.left_grip); if (! flags.resizing) { XGrabServer(blackbox->getXDisplay()); XGrabPointer(blackbox->getXDisplay(), me->window, False, ButtonMotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, ((left) ? blackbox->getLowerLeftAngleCursor() : blackbox->getLowerRightAngleCursor()), CurrentTime); flags.resizing = True; int gw, gh; frame.grab_x = me->x; frame.grab_y = me->y; frame.changing = frame.rect; constrain((left) ? TopRight : TopLeft, &gw, &gh); XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); screen->showGeometry(gw, gh); } else { XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); int gw, gh; Corner anchor; if (left) { anchor = TopRight; frame.changing.setCoords(me->x_root - frame.grab_x, frame.rect.top(), frame.rect.right(), frame.rect.bottom()); frame.changing.setHeight(frame.rect.height() + (me->y - frame.grab_y)); } else { anchor = TopLeft; frame.changing.setSize(frame.rect.width() + (me->x - frame.grab_x), frame.rect.height() + (me->y - frame.grab_y)); } constrain(anchor, &gw, &gh); XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); screen->showGeometry(gw, gh); } } } #ifdef SHAPE void BlackboxWindow::shapeEvent(XShapeEvent *) { if (blackbox->hasShapeExtensions() && flags.shaped) { configureShape(); } } #endif // SHAPE bool BlackboxWindow::validateClient(void) const { XSync(blackbox->getXDisplay(), False); XEvent e; if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window, DestroyNotify, &e) || XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window, UnmapNotify, &e)) { XPutBackEvent(blackbox->getXDisplay(), &e); return False; } return True; } void BlackboxWindow::restore(bool remap) { XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete); XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask); XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask); restoreGravity(); XUnmapWindow(blackbox->getXDisplay(), frame.window); XUnmapWindow(blackbox->getXDisplay(), client.window); XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw); XEvent ev; if (XCheckTypedWindowEvent(blackbox->getXDisplay(), 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(blackbox->getXDisplay(), client.window, screen->getRootWindow(), client.rect.x(), client.rect.y()); } if (remap) XMapWindow(blackbox->getXDisplay(), client.window); } // timer for autoraise void BlackboxWindow::timeout(void) { screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); } void BlackboxWindow::changeBlackboxHints(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: // clear all decorations except close decorations &= Decor_Close; break; default: case DecorNormal: decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify; decorations = ((functions & Func_Resize) && !isTransient() ? decorations | Decor_Handle : decorations &= ~Decor_Handle); decorations = (functions & Func_Maximize ? decorations | Decor_Maximize : decorations &= ~Decor_Maximize); break; case DecorTiny: decorations |= Decor_Titlebar | Decor_Iconify; decorations &= ~(Decor_Border | Decor_Handle); decorations = (functions & Func_Maximize ? decorations | Decor_Maximize : decorations &= ~Decor_Maximize); break; case DecorTool: decorations |= Decor_Titlebar; decorations &= ~(Decor_Iconify | Decor_Border); decorations = ((functions & Func_Resize) && !isTransient() ? decorations | Decor_Handle : decorations &= ~Decor_Handle); decorations = (functions & Func_Maximize ? decorations | Decor_Maximize : decorations &= ~Decor_Maximize); break; } // we can not be shaded if we lack a titlebar if (flags.shaded && ! (decorations & Decor_Titlebar)) shade(); if (frame.window) { XMapSubwindows(blackbox->getXDisplay(), frame.window); XMapWindow(blackbox->getXDisplay(), frame.window); } reconfigure(); setState(current_state); } } /* * 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(); if (i18n.multibyte()) frame.title_h = (style->fontset_extents->max_ink_extent.height + (frame.bevel_w * 2) + 2); else frame.title_h = (style->font->ascent + style->font->descent + (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). * * 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, int *pw, 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); 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 if (dw < static_cast(client.min_width)) dw = client.min_width; if (dh < static_cast(client.min_height)) dh = client.min_height; if (dw > static_cast(client.max_width)) dw = client.max_width; if (dh > static_cast(client.max_height)) dh = client.max_height; dw -= base_width; dw /= client.width_inc; dh -= base_height; dh /= client.height_inc; if (pw) *pw = dw; if (ph) *ph = dh; dw *= client.width_inc; dw += base_width; 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 switch (anchor) { case TopLeft: // nothing to do break; case TopRight: int dx = frame.rect.right() - frame.changing.right(); frame.changing.setPos(frame.changing.x() + dx, frame.changing.y()); break; } } int WindowStyle::doJustify(const char *text, int &start_pos, unsigned int max_length, unsigned int modifier, bool multibyte) const { size_t text_len = strlen(text); unsigned int length; do { if (multibyte) { XRectangle ink, logical; XmbTextExtents(fontset, text, text_len, &ink, &logical); length = logical.width; } else { length = XTextWidth(font, text, text_len); } length += 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; } return text_len; } BWindowGroup::BWindowGroup(Blackbox *b, Window _group) : blackbox(b), group(_group) { XWindowAttributes wattrib; if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) { // group window doesn't seem to exist anymore delete this; return; } /* watch for destroy notify on the group window (in addition to any other events we are looking for) since some managed windows can also be window group controllers, we need to make sure that we don't clobber the event mask for the managed window */ XSelectInput(blackbox->getXDisplay(), group, wattrib.your_event_mask | 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)? while (ret) { if (ret->getScreen() == screen && ret->getGroupWindow() == group) { if (ret->isTransient() && allow_transients) break; else if (! ret->isTransient()) break; } ret = ret->getTransientFor(); } 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) { if (ret->isTransient() && allow_transients) break; else if (! ret->isTransient()) break; } } return ret; }