diff --git a/atoms.cpp b/atoms.cpp index db931d8d3c..41b16ea318 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -4,21 +4,53 @@ Atoms::Atoms() { - //TODO use XInternAtoms instead to avoid roundtrips - wm_protocols = XInternAtom(qt_xdisplay(), "WM_PROTOCOLS", FALSE); - wm_delete_window = XInternAtom(qt_xdisplay(), "WM_DELETE_WINDOW", FALSE); - wm_take_focus = XInternAtom(qt_xdisplay(), "WM_TAKE_FOCUS", FALSE); - + const int max = 20; + Atom* atoms[max]; + char* names[max]; + Atom atoms_return[max]; + int n = 0; + + atoms[n] = &wm_protocols; + names[n++] = "WM_PROTOCOLS"; + + atoms[n] = &wm_delete_window; + names[n++] = "WM_DELETE_WINDOW"; + + atoms[n] = &wm_take_focus; + names[n++] = "WM_TAKE_FOCUS"; + + atoms[n] = &wm_change_state; + names[n++] = "WM_CHANGE_STATE"; + // compatibility - kwm_win_icon = XInternAtom(qt_xdisplay(), "KWM_WIN_ICON", FALSE); - kwm_running = XInternAtom(qt_xdisplay(), "KWM_RUNNING", FALSE); - - - net_number_of_desktops = XInternAtom(qt_xdisplay(), "_NET_NUMBER_OF_DESKTOPS", False); - net_current_desktop = XInternAtom(qt_xdisplay(), "_NET_CURRENT_DESKTOP", False); - net_active_window = XInternAtom(qt_xdisplay(), "_NET_ACTIVE_WINDOW", False); - - net_client_list = XInternAtom(qt_xdisplay(), "_NET_CLIENT_LIST", False); - net_client_list_stacking = XInternAtom(qt_xdisplay(), "_NET_CLIENT_LIST_STACKIN", False); - net_kde_docking_windows = XInternAtom(qt_xdisplay(), "_NET_KDE_DOCKING_WINDOWS", False); + atoms[n] = &kwm_win_icon; + names[n++] = "KWM_WIN_ICON"; + + // compatibility + atoms[n] = &kwm_running; + names[n++] = "KWM_RUNNING"; + + atoms[n] = &net_number_of_desktops; + names[n++] = "_NET_NUMBER_OF_DESKTOPS"; + + atoms[n] = &net_current_desktop; + names[n++] = "_NET_CURRENT_DESKTOP"; + + atoms[n] = &net_active_window; + names[n++] = "_NET_ACTIVE_WINDOW"; + + atoms[n] = &net_client_list; + names[n++] = "_NET_CLIENT_LIST"; + + atoms[n] = &net_client_list_stacking; + names[n++] = "_NET_CLIENT_LIST_STACKING"; + + atoms[n] = &net_kde_docking_windows; + names[n++] = "_NET_KDE_DOCKING_WINDOWS"; + + XInternAtoms( qt_xdisplay(), names, n, FALSE, atoms_return ); + for (int i = 0; i < n; i++ ) + *atoms[i] = atoms_return[i]; + + } diff --git a/atoms.h b/atoms.h index 46ea04a200..e901d6776c 100644 --- a/atoms.h +++ b/atoms.h @@ -9,6 +9,7 @@ public: Atom wm_protocols; Atom wm_delete_window; Atom wm_take_focus; + Atom wm_change_state; Atom kwm_win_icon; // compatibility Atom kwm_running; diff --git a/client.cpp b/client.cpp index e5b196ac37..72477412e8 100644 --- a/client.cpp +++ b/client.cpp @@ -289,7 +289,7 @@ Client::Client( Workspace *ws, WId w, QWidget *parent, const char *name, WFlags is_shape = FALSE; is_sticky = FALSE; - getIcons(); + getWMHints(); getWindowProtocols(); getWmNormalHints(); // get xSizeHint fetchName(); @@ -481,6 +481,8 @@ bool Client::windowEvent( XEvent * e) break; case ReparentNotify: break; + case ClientMessage: + return clientMessage( e->xclient ); default: break; } @@ -632,13 +634,13 @@ bool Client::propertyNotify( XPropertyEvent& e ) transient_for = None; break; case XA_WM_HINTS: - getIcons(); + getWMHints(); break; default: if ( e.atom == atoms->wm_protocols ) getWindowProtocols(); else if ( e.atom == atoms->kwm_win_icon ) { - getIcons(); + getWMHints(); // for the icons } break; @@ -647,6 +649,24 @@ bool Client::propertyNotify( XPropertyEvent& e ) } +/*! + Handles client messages for the client window +*/ +bool Client::clientMessage( XClientMessageEvent& e ) +{ + if ( e.message_type == atoms->wm_change_state) { + if ( e.data.l[0] == IconicState && isNormal() ) + iconify(); + return TRUE; + } else if ( e.message_type == atoms->net_active_window ) { + workspace()->activateClient( this ); + return TRUE; + } + + return FALSE; +} + + /*! Auxiliary function to inform the client about the current window configuration. @@ -1125,6 +1145,7 @@ void Client::iconify() setMappingState( IconicState ); hide(); // TODO animation (virtual function) + workspace()->iconifyOrDeiconifyTransientsOf( this ); } void Client::closeWindow() @@ -1458,12 +1479,20 @@ void Client::setDesktop( int desktop) KWM::moveToDesktop( win, desk );//##### compatibility } -void Client::getIcons() +void Client::getWMHints() { icon_pix = KWM::icon( win, 32, 32 ); // TODO sizes from workspace miniicon_pix = KWM::miniIcon( win, 16, 16 ); if ( !isWithdrawn() ) iconChange(); + + input = TRUE; + XWMHints *hints = XGetWMHints(qt_xdisplay(), win ); + if ( hints ) { + if ( hints->flags & InputHint ) + input = hints->input; + XFree((char*)hints); + } } void Client::getWindowProtocols(){ @@ -1490,7 +1519,8 @@ void Client::getWindowProtocols(){ */ void Client::takeFocus() { - XSetInputFocus( qt_xdisplay(), win, RevertToPointerRoot, CurrentTime ); + if ( input ) + XSetInputFocus( qt_xdisplay(), win, RevertToPointerRoot, CurrentTime ); if ( Ptakefocus ) sendClientMessage(win, atoms->wm_protocols, atoms->wm_take_focus); } @@ -1505,6 +1535,25 @@ void Client::setMask( const QRegion & reg) } +/*! + Returns the main client. For normal windows, this is the window + itself. For transient windows, it is the parent. + + */ +Client* Client::mainClient() +{ + if ( !isTransient() ) + return this; + ClientList saveset; + Client* c = this; + do { + saveset.append( c ); + c = workspace()->findClient( c->transientFor() ); + } while ( c && c->isTransient() && !saveset.contains( c ) ); + + return c?c:this; +} + NoBorderClient::NoBorderClient( Workspace *ws, WId w, QWidget *parent, const char *name ) : Client( ws, w, parent, name ) diff --git a/client.h b/client.h index d9d93af59d..5899dc4d10 100644 --- a/client.h +++ b/client.h @@ -20,7 +20,7 @@ public: WindowWrapper( WId w, Client *parent=0, const char* name=0); ~WindowWrapper(); - inline WId window() const; + WId window() const; void releaseWindow(); void invalidateWindow(); QSize sizeHint() const; @@ -56,12 +56,14 @@ public: Client( Workspace *ws, WId w, QWidget *parent=0, const char *name=0, WFlags f = 0); ~Client(); - inline WId window() const; - inline WindowWrapper* windowWrapper() const; - inline Workspace* workspace() const; + WId window() const; + WindowWrapper* windowWrapper() const; + Workspace* workspace() const; void releaseWindow(); void invalidateWindow(); - inline WId transientFor() const; + WId transientFor() const; + bool isTransient() const; + Client* mainClient(); virtual bool windowEvent( XEvent * ); @@ -81,8 +83,8 @@ public: int maximumWidth() const; int maximumHeight() const; - inline QPixmap icon() const; - inline QPixmap miniIcon() const; + QPixmap icon() const; + QPixmap miniIcon() const; // is the window in withdrawn state? @@ -98,7 +100,7 @@ public: return state == NormalState; } - inline bool isActive() const; + bool isActive() const; void setActive( bool ); int desktop() const; @@ -108,10 +110,10 @@ public: bool isShade() const; virtual void setShade( bool ); - inline bool isMaximized() const; + bool isMaximized() const; enum MaximizeMode { MaximizeVertical, MaximizeHorizontal, MaximizeFull }; - inline bool isSticky() const; + bool isSticky() const; void setSticky( bool ); void takeFocus(); @@ -183,6 +185,7 @@ protected: bool unmapNotify( XUnmapEvent& e ); bool configureRequest( XConfigureRequestEvent& e ); bool propertyNotify( XPropertyEvent& e ); + bool clientMessage( XClientMessageEvent& e ); private: QSize sizeForWindowSize( const QSize&, bool ignore_height = FALSE ) const; @@ -217,10 +220,11 @@ private: WId transient_for; bool is_sticky; bool is_shape; - void getIcons(); + void getWMHints(); void getWindowProtocols(); uint Pdeletewindow :1; // does the window understand the DeleteWindow protocol? uint Ptakefocus :1;// does the window understand the TakeFocus protocol? + uint input :1; // does the window want input in its wm_hints uint mapped :1; // keeps track of our visiblity within the asynchronous event flow QPixmap icon_pix; QPixmap miniicon_pix; @@ -248,6 +252,13 @@ inline WId Client::transientFor() const return transient_for; } +inline bool Client::isTransient() const +{ + return transient_for != 0; +} + + + inline int Client::mappingState() const { return state; diff --git a/stdclient.cpp b/stdclient.cpp index 13dac9b325..4963b9be5d 100644 --- a/stdclient.cpp +++ b/stdclient.cpp @@ -32,7 +32,7 @@ static QPixmap* dis_menu_pix = 0; static bool pixmaps_created = FALSE; static void create_pixmaps(); -QPixmap* kwin_get_menu_pix_hack() +QPixmap* kwin_get_menu_pix_hack() { create_pixmaps(); return menu_pix; @@ -241,7 +241,7 @@ StdClient::StdClient( Workspace *ws, WId w, QWidget *parent, const char *name ) button[1]->setIconSet(isSticky() ? isActive() ? *pindown_pix : *dis_pindown_pix : isActive() ? *pinup_pix : *dis_pinup_pix ); connect( button[1], SIGNAL( clicked() ), this, ( SLOT( toggleSticky() ) ) ); - button[1]->hide(); // no sticky + button[1]->hide(); // no sticky for now button[2]->hide(); button[3]->setIconSet(isActive() ? *minimize_pix : *dis_minimize_pix); @@ -250,7 +250,14 @@ StdClient::StdClient( Workspace *ws, WId w, QWidget *parent, const char *name ) connect( button[4], SIGNAL( clicked(int) ), this, ( SLOT( maxButtonClicked(int) ) ) ); button[5]->setIconSet(isActive() ? *close_pix : *dis_close_pix); connect( button[5], SIGNAL( clicked() ), this, ( SLOT( closeWindow() ) ) ); - + + if ( isTransient() ) { + // lighter decoration for transient windows + button[1]->hide(); + button[2]->hide(); + button[3]->hide(); + button[4]->hide(); + } } void StdClient::activeChange(bool on) diff --git a/tabbox.cpp b/tabbox.cpp index 415dab14ee..180d095015 100644 --- a/tabbox.cpp +++ b/tabbox.cpp @@ -51,8 +51,8 @@ void TabBox::reset() QFontMetrics fm( fontMetrics() ); int cw = 0; while ( c ) { - // TODO consider options_traverse_all - if ( options_traverse_all ||c->isOnDesktop(workspace()->currentDesktop()) ) { + if ( (options_traverse_all ||c->isOnDesktop(workspace()->currentDesktop())) + && (!c->isIconified() || c->mainClient() == c ) ) { if ( client == c ) clients.prepend( c ); else @@ -94,8 +94,11 @@ void TabBox::nextPrev( bool next) else client = workspace()->previousClient(client); } while (client != sign && client && - !options_traverse_all && - !client->isOnDesktop(workspace()->currentDesktop())); + ( !options_traverse_all && !client->isOnDesktop(workspace()->currentDesktop()) ) + || + ( client->isIconified() && client->mainClient() != client ) + ); + if (!options_traverse_all && client && !client->isOnDesktop(workspace()->currentDesktop())) diff --git a/workspace.cpp b/workspace.cpp index 2a4c98f777..fb2860eafa 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -747,10 +747,31 @@ void Workspace::activateClient( Client* c) } raiseClient( c ); c->show(); + iconifyOrDeiconifyTransientsOf( c ); if ( options->focusPolicyIsReasonable() ) requestFocus( c ); } +void Workspace::iconifyOrDeiconifyTransientsOf( Client* c ) +{ + if ( c->isIconified() ) { + for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { + if ( (*it)->transientFor() == c->window() && !(*it)->isIconified() ) { + (*it)->setMappingState( IconicState ); + (*it)->hide(); + iconifyOrDeiconifyTransientsOf( (*it) ); + } + } + } else { + for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { + if ( (*it)->transientFor() == c->window() && !(*it)->isVisible() ) { + (*it)->show(); + iconifyOrDeiconifyTransientsOf( (*it) ); + } + } + } +} + /*! Tries to activate the client by asking X for the input focus. This @@ -1346,17 +1367,11 @@ void Workspace::setNumberOfDesktops( int n ) */ bool Workspace::clientMessage( XClientMessageEvent msg ) { - if ( msg.message_type == atoms->net_active_window ) { - Client * c = findClient( msg.data.l[0] ); - if ( c ) { - activateClient( c ); - return TRUE; - } - } else if ( msg.message_type == atoms->net_current_desktop ) { + if ( msg.message_type == atoms->net_current_desktop ) { setCurrentDesktop( msg.data.l[0] ); return TRUE; } - + return FALSE; } @@ -1525,6 +1540,15 @@ void Workspace::sendToDesktop( int desk ) popup_client->setDesktop( desk ); popup_client->hide(); + + Client* old = popup_client; + for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { + if ( (*it)->transientFor() == popup_client->window() ) { + popup_client = *it; + sendToDesktop( desk ); + popup_client = old; + } + } } void Workspace::slotWindowOperations() @@ -1555,24 +1579,24 @@ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) if (options->windowSnapZone() || options->borderSnapZone()) { int snap; //snap trigger - + QRect maxRect = clientArea(); int xmin = maxRect.left(); int xmax = maxRect.right(); //desk size int ymin = maxRect.top(); int ymax = maxRect.bottom(); int cx, cy, rx, ry, cw, ch; //these don't change - + int nx, ny; //buffers int deltaX = xmax, deltaY = ymax; //minimum distance to other clients - + int lx, ly, lrx, lry; //coords and size for the comparison client, l - + nx = cx = pos.x(); ny = cy = pos.y(); rx = cx + (cw = c->width()); ry = cy + (ch = c->height()); - + // border snap snap = options->borderSnapZone(); if (snap) { @@ -1584,7 +1608,7 @@ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) deltaX = abs(xmax-rx); nx = xmax - cw; } - + if ( QABS(cy-ymin) < snap ){ deltaY = QABS(cy-ymin); ny = ymin; @@ -1594,7 +1618,7 @@ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) ny = ymax - ch; } } - + // windows snap snap = options->windowSnapZone(); if (snap) { @@ -1607,7 +1631,7 @@ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) ly = (*l)->y(); lrx = lx + (*l)->width(); lry = ly + (*l)->height(); - + if( ( ( cy <= lry ) && ( cy >= ly ) ) || ( ( ry >= ly ) && ( ry <= lry ) ) || ( ( ly >= cy ) && ( lry <= ry ) ) ) { @@ -1622,7 +1646,7 @@ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) nx = lx - cw; } } - + if( ( ( cx <= lrx ) && ( cx >= lx ) ) || ( ( rx >= lx ) && ( rx <= lrx ) ) || ( ( lx >= cx ) && ( lrx <= rx ) ) ) { diff --git a/workspace.h b/workspace.h index 4322f882f3..d6ab2822cb 100644 --- a/workspace.h +++ b/workspace.h @@ -98,6 +98,7 @@ public: void makeFullScreen( Client* ); bool iconifyMeansWithdraw( Client* ); + void iconifyOrDeiconifyTransientsOf( Client* ); public slots: void setCurrentDesktop( int new_desktop );