Say hello to kwin. WARNING: NOT USABLE YET. See README.

svn path=/trunk/kdebase/kwin/; revision=27871
This commit is contained in:
Matthias Ettrich 1999-08-19 23:26:42 +00:00
commit 311db796c6
18 changed files with 3848 additions and 0 deletions

20
README Normal file
View file

@ -0,0 +1,20 @@
Fri Aug 20 01:30:50 CEST 1999
This is the beginning of kwin, kwm next generation.
WARNING: this thing is hardly usable now, neither ICCCM nor KDE
compliant yet!
All it has is a context menu that allows you to switch between two
decoration styles, KDE classic and an experimental style.
Please don't work on the code, I'll finish it during my summer
vacations (four weeks from now on).
kwin was only commited to allow people like Mosfet to have a look at
the Client API (and StdClient) to write nifty new themable decorations.
Have fun,
Matthias
<ettrich@kde.org>

15
atoms.cpp Normal file
View file

@ -0,0 +1,15 @@
#include <qapplication.h>
#include "atoms.h"
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);
// compatibility
kwm_win_icon = XInternAtom(qt_xdisplay(), "KWM_WIN_ICON", FALSE);
}

19
atoms.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef ATOMS_H
#define ATOMS_H
#include <X11/Xlib.h>
class Atoms {
public:
Atoms();
Atom wm_protocols;
Atom wm_delete_window;
Atom wm_take_focus;
Atom kwm_win_icon; // compatibility
};
extern Atoms* atoms;
#endif

213
beclient.cpp Normal file
View file

@ -0,0 +1,213 @@
#include "beclient.h"
#include <qapplication.h>
#include <qcursor.h>
#include <qabstractlayout.h>
#include <qlayout.h>
#include <qtoolbutton.h>
#include <qlabel.h>
#include <qdrawutil.h>
#include "workspace.h"
static const char * size_xpm[] = {
/* width height num_colors chars_per_pixel */
"16 16 3 1",
/* colors */
" s None c None",
". c #707070",
"X c white",
/* pixels */
" ",
" ....... ",
" .XXXXXX ",
" .X .X ",
" .X .X....... ",
" .X .XXXXXXXX ",
" .X .X .X ",
" .X....X .X ",
" .XXXXXX .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X........X ",
" .XXXXXXXXXX ",
" ",
" "};
static QPixmap* size_pix = 0;
static bool pixmaps_created = FALSE;
static void create_pixmaps()
{
if ( pixmaps_created )
return;
size_pix = new QPixmap( size_xpm );
}
BeClient::BeClient( Workspace *ws, WId w, QWidget *parent, const char *name )
: Client( ws, w, parent, name, WResizeNoErase )
{
create_pixmaps();
QFont f = font();
f.setBold( TRUE );
setFont( f );
QGridLayout* g = new QGridLayout( this, 0, 0, 2 );
g->addRowSpacing(1, 2);
g->setRowStretch( 2, 10 );
g->addWidget( windowWrapper(), 2, 1 );
g->addColSpacing(0, 2);
g->addColSpacing(2, 2);
g->addRowSpacing(3, 2);
QHBoxLayout* hb = new QHBoxLayout;
g->addLayout( hb, 0, 1 );
int fh = QMAX( 16, fontMetrics().lineSpacing());
titlebar = new QSpacerItem(40, fh, QSizePolicy::Preferred,
QSizePolicy::Minimum );
hb->addItem( titlebar );
hb->addStretch();
}
BeClient::~BeClient()
{
}
void BeClient::resizeEvent( QResizeEvent* e)
{
Client::resizeEvent( e );
doShape();
if ( isVisibleToTLW() ) {
// manual clearing without the titlebar (we selected WResizeNoErase )
QPainter p( this );
QRect t = titlebar->geometry();
t.setTop( 0 );
t.setLeft( 0 );
QRegion r = rect();
r = r.subtract( t );
p.setClipRegion( r );
p.eraseRect( rect() );
}
}
/*!\reimp
*/
void BeClient::captionChange( const QString& )
{
doShape();
repaint();
}
void BeClient::paintEvent( QPaintEvent* )
{
QPainter p( this );
QRect bar ( 0, 0, titlebar->geometry().right()+1, titlebar->geometry().bottom() );
qDrawWinPanel( &p, 0, bar.bottom()+2, width(), height() - bar.bottom()-2, colorGroup(), FALSE );
qDrawWinPanel( &p, 2, bar.bottom()+4, width()-4, height() - bar.bottom()-6, colorGroup(), TRUE );
QRect t = titlebar->geometry();
bar.setBottom( bar.bottom() + 3 );
p.setClipRect( bar );
bar.setBottom( bar.bottom() + 2 );
if ( isActive() ) {
QPalette pal( QColor(248,204,0) );
qDrawWinPanel( &p, bar, pal.normal(), FALSE, &pal.brush(QPalette::Normal, QColorGroup::Background ) );
}
else
qDrawWinPanel( &p, bar, colorGroup(), FALSE, &colorGroup().brush( QColorGroup::Background ) );
p.setClipping( FALSE );
p.drawPixmap( t.right() - 20, t.center().y()-8, *size_pix );
p.drawPixmap( t.left() +4, t.center().y()-miniIcon().height()/2, miniIcon() );
t.setLeft( t.left() + 20 +10);
p.drawText( t, AlignLeft|AlignVCenter, caption() );
}
void BeClient::showEvent( QShowEvent* e)
{
Client::showEvent( e );
doShape();
repaint();
}
void BeClient::doShape()
{
QFontMetrics fm = fontMetrics();
int cap = 20+20+10+10+fm.boundingRect(caption() ).width();
titlebar->changeSize( QMIN( width(), cap), QMAX( 16, fm.lineSpacing()),
QSizePolicy::Preferred, QSizePolicy::Minimum );
layout()->activate(); //#### this is broken!!!!! PAUL!!!!!
// // // do it manually: #######remove this for Qt-2.01
titlebar->setGeometry( QRect( titlebar->geometry().x(), titlebar->geometry().y(),
titlebar->sizeHint().width(), titlebar->sizeHint().height() ) );
QRegion r( rect() );
r = r.subtract( QRect( QPoint( titlebar->geometry().right()+1, 0), QPoint( width(), titlebar->geometry().bottom()) ) );
setMask( r );
}
/*!\reimp
*/
void BeClient::activeChange( bool /* act */ )
{
repaint( 0, 0, width(), titlebar->geometry().bottom()+3, FALSE );
}
/*!\reimp
*/
Client::MousePosition BeClient::mousePosition( const QPoint& p ) const
{
const int range = 16;
const int border = 4;
int ly = titlebar->geometry().bottom();
int lx = titlebar->geometry().right();
if ( p.x() > titlebar->geometry().right() ) {
if ( p.y() <= ly + range && p.x() >= width()-range)
return TopRight;
else if ( p.y() <= ly + border )
return Top;
} else if ( p.y() < ly ) {
if ( p.y() > border && p.x() < lx - border )
return Client::mousePosition( p );
if ( p.y() < range && p.x() > lx - range )
return TopRight;
else if ( p.x() > lx-border )
return Right;
}
return Client::mousePosition( p );
}
void BeClient::mousePressEvent( QMouseEvent * e )
{
Client::mousePressEvent( e );
}
void BeClient::mouseReleaseEvent( QMouseEvent * e )
{
workspace()->makeFullScreen( this );
Client::mouseReleaseEvent( e );
}
void BeClient::mouseDoubleClickEvent( QMouseEvent * e )
{
if ( titlebar->geometry().contains( e->pos() ) )
setShade( !isShade() );
workspace()->requestFocus( this );
}

37
beclient.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef BECLIENT_H
#define BECLIENT_H
#include "client.h"
class QToolButton;
class QLabel;
class QSpacerItem;
class BeClient : public Client
{
Q_OBJECT
public:
BeClient( Workspace *ws, WId w, QWidget *parent=0, const char *name=0 );
~BeClient();
protected:
void resizeEvent( QResizeEvent* );
void paintEvent( QPaintEvent* );
void mousePressEvent( QMouseEvent * );
void mouseReleaseEvent( QMouseEvent * );
void mouseDoubleClickEvent( QMouseEvent * e );
void captionChange( const QString& name );
void showEvent( QShowEvent* );
void activeChange( bool );
MousePosition mousePosition( const QPoint& p ) const;
private:
QSpacerItem* titlebar;
void doShape();
};
#endif

1464
client.cpp Normal file

File diff suppressed because it is too large Load diff

285
client.h Normal file
View file

@ -0,0 +1,285 @@
#ifndef CLIENT_H
#define CLIENT_H
#include "options.h"
#include <qframe.h>
#include <qvbox.h>
#include <qpixmap.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
class Workspace;
class Client;
class KWM
{
public:
static QPixmap miniIcon(Window w, int width=0, int height=0);
static QPixmap icon(Window w, int width=0, int height=0);
};
class WindowWrapper : public QWidget
{
Q_OBJECT
public:
WindowWrapper( WId w, Client *parent=0, const char* name=0);
~WindowWrapper();
inline WId window() const;
void releaseWindow();
void invalidateWindow();
QSize sizeHint() const;
QSizePolicy sizePolicy() const;
protected:
void resizeEvent( QResizeEvent * );
void showEvent( QShowEvent* );
void hideEvent( QHideEvent* );
void mousePressEvent( QMouseEvent* );
void mouseReleaseEvent( QMouseEvent* );
void mouseMoveEvent( QMouseEvent* );
bool x11Event( XEvent * ); // X11 event
private:
WId win;
Time lastMouseEventTime;
};
inline WId WindowWrapper::window() const
{
return win;
}
class Client : public QWidget
{
Q_OBJECT
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;
void releaseWindow();
void invalidateWindow();
inline WId transientFor() const;
virtual bool windowEvent( XEvent * );
void manage( bool isMapped = FALSE );
void setMappingState( int s );
int mappingState() const;
void requestActivation();
void withdraw();
QSize adjustedSize( const QSize& ) const;
QSize minimumSize() const;
int minimumWidth() const;
int minimumHeight() const;
QSize maximumSize() const;
int maximumWidth() const;
int maximumHeight() const;
inline QPixmap icon() const;
inline QPixmap miniIcon() const;
// is the window in withdrawn state?
bool isWithdrawn(){
return state == WithdrawnState;
}
// is the window in iconic state?
bool isIconified(){
return state == IconicState;
}
// is the window in normal state?
bool isNormal(){
return state == NormalState;
}
inline bool isActive() const;
void setActive( bool );
int desktop() const;
bool isOnDesktop( int d ) const;
bool isShade() const;
virtual void setShade( bool );
inline bool isMaximized() const;
enum MaximizeMode { MaximizeVertical, MaximizeHorizontal, MaximizeFull };
inline bool isSticky() const;
void setSticky( bool );
void takeFocus();
public slots:
void iconify();
void closeWindow();
void maximize( MaximizeMode );
void maximize();
void fullScreen();
void toggleSticky();
protected:
void paintEvent( QPaintEvent * );
void mousePressEvent( QMouseEvent * );
void mouseReleaseEvent( QMouseEvent * );
void mouseMoveEvent( QMouseEvent * );
void enterEvent( QEvent * );
void leaveEvent( QEvent * );
void moveEvent( QMoveEvent * );
void showEvent( QShowEvent* );
void hideEvent( QHideEvent* );
bool x11Event( XEvent * ); // X11 event
bool eventFilter( QObject *, QEvent * );
virtual void init();
virtual void captionChange( const QString& name );
virtual void iconChange();
virtual void activeChange( bool );
virtual void maximizeChange( bool );
virtual void stickyChange( bool );
enum MousePosition {
Nowhere, TopLeft , BottomRight, BottomLeft, TopRight, Top, Bottom, Left, Right, Center
};
virtual MousePosition mousePosition( const QPoint& ) const;
virtual void setMouseCursor( MousePosition m );
// handlers for X11 events
bool mapRequest( XMapRequestEvent& e );
bool unmapNotify( XUnmapEvent& e );
bool configureRequest( XConfigureRequestEvent& e );
bool propertyNotify( XPropertyEvent& e );
private:
QSize sizeForWindowSize( const QSize&, bool ignore_height = FALSE ) const;
void getWmNormalHints();
void fetchName();
void gravitate( bool invert );
WId win;
WindowWrapper* wwrap;
Workspace* wspace;
int desk;
bool buttonDown;
MousePosition mode;
QPoint moveOffset;
QPoint invertedMoveOffset;
QSize clientSize;
XSizeHints xSizeHint;
void sendSynteticConfigureNotify();
int state;
bool active;
int ignore_unmap;
QRect original_geometry;
bool shaded;
WId transient_for;
bool is_sticky;
void getIcons();
void getWindowProtocols();
uint Pdeletewindow :1; // does the window understand the DeleteWindow protocol?
uint Ptakefocus :1;// does the window understand the TakeFocus protocol?
QPixmap icon_pix;
QPixmap miniicon_pix;
QRect geom_restore;
};
inline WId Client::window() const
{
return win;
}
inline WindowWrapper* Client::windowWrapper() const
{
return wwrap;
}
inline Workspace* Client::workspace() const
{
return wspace;
}
inline WId Client::transientFor() const
{
return transient_for;
}
inline int Client::mappingState() const
{
return state;
}
inline bool Client::isActive() const
{
return active;
}
/*!
Returns the virtual desktop within the workspace() the client window
is located in, -1 if it isn't located on any special desktop. This may be
if the window wasn't mapped yet or if the window is sticky. Do not use
desktop() directly, use isOnDesktop() instead.
*/
inline int Client::desktop() const
{
return desk;
}
/*!
Returns whether the client is on visible or iconified on the virtual
desktop \a d. This is always TRUE for sticky clients.
*/
inline bool Client::isOnDesktop( int d ) const
{
return desk == d || desk == -1 || isSticky();
}
inline QPixmap Client::icon() const
{
return icon_pix;
}
inline QPixmap Client::miniIcon() const
{
return miniicon_pix;
}
/*!
Is the client maximized?
*/
inline bool Client::isMaximized() const
{
return !geom_restore.isNull();
}
inline bool Client::isSticky() const
{
return is_sticky;
}
class NoBorderClient : public Client
{
Q_OBJECT
public:
NoBorderClient( Workspace *ws, WId w, QWidget *parent=0, const char *name=0 );
~NoBorderClient();
};
#endif

BIN
kwin Executable file

Binary file not shown.

18
kwin.pro Normal file
View file

@ -0,0 +1,18 @@
TEMPLATE = app
CONFIG = qt warn_on release
HEADERS = atoms.h \
beclient.h \
client.h \
main.h \
options.h \
stdclient.h \
tabbox.h \
workspace.h
SOURCES = atoms.cpp \
beclient.cpp \
client.cpp \
main.cpp \
stdclient.cpp \
tabbox.cpp \
workspace.cpp
TARGET = kwin

113
main.cpp Normal file
View file

@ -0,0 +1,113 @@
#include "main.h"
#include "options.h"
#include "atoms.h"
#include "workspace.h"
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <signal.h>
#define INT8 _X11INT8
#define INT32 _X11INT32
#include <X11/Xproto.h>
#undef INT8
#undef INT32
#define i18n(x) (x)
Options* options;
Atoms* atoms;
static bool initting = FALSE;
int x11ErrorHandler(Display *d, XErrorEvent *e){
char msg[80], req[80], number[80];
bool ignore_badwindow = FALSE; //maybe temporary
if (initting &&
(
e->request_code == X_ChangeWindowAttributes
|| e->request_code == X_GrabKey
)
&& (e->error_code == BadAccess)) {
fprintf(stderr, i18n("kwin: it looks like there's already a window manager running. kwin not started\n"));
exit(1);
}
if (ignore_badwindow && (e->error_code == BadWindow || e->error_code == BadColor))
return 0;
XGetErrorText(d, e->error_code, msg, sizeof(msg));
sprintf(number, "%d", e->request_code);
XGetErrorDatabaseText(d, "XRequest", number, "<unknown>", req, sizeof(req));
fprintf(stderr, "kwin: %s(0x%lx): %s\n", req, e->resourceid, msg);
if (initting) {
fprintf(stderr, i18n("kwin: failure during initialisation; aborting\n"));
exit(1);
}
return 0;
}
Application::Application( int &argc, char *argv[] )
: QApplication( argc, argv )
{
initting = TRUE;
options = new Options;
atoms = new Atoms;
// install X11 error handler
XSetErrorHandler( x11ErrorHandler );
// create a workspace.
workspaces += new Workspace();
initting = FALSE;
if ( argc > 1 ) {
QString s = argv[1];
int i = s.toInt();
workspaces += new Workspace( (WId ) i );
}
syncX();
initting = FALSE;
}
Application::~Application()
{
for ( WorkspaceList::Iterator it = workspaces.begin(); it != workspaces.end(); ++it) {
delete (*it);
}
}
bool Application::x11EventFilter( XEvent *e )
{
for ( WorkspaceList::Iterator it = workspaces.begin(); it != workspaces.end(); ++it) {
if ( (*it)->workspaceEvent( e ) )
return TRUE;
}
return FALSE;
}
static void sighandler(int) {
QApplication::exit();
}
int main( int argc, char * argv[] ) {
if (signal(SIGTERM, sighandler) == SIG_IGN)
signal(SIGTERM, SIG_IGN);
if (signal(SIGINT, sighandler) == SIG_IGN)
signal(SIGINT, SIG_IGN);
if (signal(SIGHUP, sighandler) == SIG_IGN)
signal(SIGHUP, SIG_IGN);
Application a( argc, argv );
fcntl(ConnectionNumber(qt_xdisplay()), F_SETFD, 1);
return a.exec();
}

23
main.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef MAIN_H
#define MAIN_H
#include <qapplication.h>
#include "workspace.h"
typedef QValueList<Workspace*> WorkspaceList;
class Application : public QApplication
{
public:
Application( int &argc, char **argv );
~Application();
protected:
bool x11EventFilter( XEvent * );
private:
WorkspaceList workspaces;
};
#endif

47
options.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef OPTIONS_H
#define OPTIONS_H
class Options {
public:
/*!
Different focus policies:
<ul>
<li>ClickToFocus - Clicking into a window activates it. This is
also the default.
<li>FocusFollowsMouse - Moving the mouse pointer actively onto a
window activates it.
<li>FocusUnderMouse - The window that happens to be under the
mouse pointer becomes active.
<li>FocusStricklyUnderMouse - Only the window under the mouse
pointer is active. If the mouse points nowhere, nothing has the
focus. In practice, this is the same as FocusUnderMouse, since
kdesktop can take the focus.
Note that FocusUnderMouse and FocusStricklyUnderMouse are not
particulary useful. They are only provided for old-fashined
die-hard UNIX people ;-)
</ul>
*/
enum FocusPolicy { ClickToFocus, FocusFollowsMouse, FocusUnderMouse, FocusStricklyUnderMouse };
FocusPolicy focusPolicy;
bool focusPolicyIsReasonable() {
return focusPolicy == ClickToFocus || focusPolicy == FocusFollowsMouse;
}
Options(){
focusPolicy = ClickToFocus;
}
};
extern Options* options;
#endif

334
stdclient.cpp Normal file
View file

@ -0,0 +1,334 @@
#include "stdclient.h"
#include <qapplication.h>
#include <qcursor.h>
#include <qabstractlayout.h>
#include <qlayout.h>
#include <qtoolbutton.h>
#include <qlabel.h>
#include <qdrawutil.h>
#include "workspace.h"
static const char * close_xpm[] = {
/* width height num_colors chars_per_pixel */
"16 16 3 1",
/* colors */
" s None c None",
". c white",
"X c #707070",
/* pixels */
" ",
" ",
" .X .X ",
" .XX .XX ",
" .XX .XX ",
" .XX .XX ",
" .XX.XX ",
" .XXX ",
" .XXX ",
" .XX.XX ",
" .XX .XX ",
" .XX .XX ",
" .XX .XX ",
" .X .X ",
" ",
" "};
static const char * maximize_xpm[] = {
/* width height num_colors chars_per_pixel */
"16 16 3 1",
/* colors */
" s None c None",
". c white",
"X c #707070",
/* pixels */
" ",
" ",
" ........... ",
" .XXXXXXXXXX ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X........X ",
" .XXXXXXXXXX ",
" ",
" ",
" "};
static const char * minimize_xpm[] = {
/* width height num_colors chars_per_pixel */
"16 16 3 1",
/* colors */
" s None c None",
". c white",
"X c #707070",
/* pixels */
" ",
" ",
" ",
" ",
" ",
" ",
" ... ",
" . X ",
" .XX ",
" ",
" ",
" ",
" ",
" ",
" ",
" "};
static const char * normalize_xpm[] = {
/* width height num_colors chars_per_pixel */
"16 16 3 1",
/* colors */
" s None c None",
". c #707070",
"X c white",
/* pixels */
" ",
" ",
" ........... ",
" .XXXXXXXXXX ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X .X ",
" .X........X ",
" .XXXXXXXXXX ",
" ",
" ",
" "};
static const char * pinup_xpm[] = {
/* width height num_colors chars_per_pixel */
"16 16 4 1",
/* colors */
" s None c None",
". c #707070",
"X c white",
"o c #a0a0a0",
/* pixels */
" ",
" ",
" ",
" .. . ",
" .X. .. ",
" .XX...X. ",
"XXXXXX.oXoXoX. ",
"oooooo.oXoXoX. ",
".......oo.o.o. ",
" .o...... ",
" ... .. ",
" .. . ",
" ",
" ",
" ",
" "};
static const char * pindown_xpm[] = {
/* width height num_colors chars_per_pixel */
"16 16 4 1",
/* colors */
" s None c None",
". c #707070",
"X c white",
"o c #a0a0a0",
/* pixels */
" ",
" ",
" .... ",
" ..XXXX. ",
" ...XXXXXX. ",
" .X.XXXooo. ",
" .XX.XXooo.. ",
" .XX..Xoo... ",
" .XXXX..... ",
" .XXXoooo.. ",
" .Xoooo... ",
" .oooo... ",
" ...... ",
" ",
" ",
" "};
static QPixmap* close_pix = 0;
static QPixmap* maximize_pix = 0;
static QPixmap* minimize_pix = 0;
static QPixmap* normalize_pix = 0;
static QPixmap* pinup_pix = 0;
static QPixmap* pindown_pix = 0;
static bool pixmaps_created = FALSE;
static void create_pixmaps()
{
if ( pixmaps_created )
return;
close_pix = new QPixmap( close_xpm );
maximize_pix = new QPixmap( maximize_xpm );
minimize_pix = new QPixmap( minimize_xpm );
normalize_pix = new QPixmap( normalize_xpm );
pinup_pix = new QPixmap( pinup_xpm );
pindown_pix = new QPixmap( pindown_xpm );
}
StdClient::StdClient( Workspace *ws, WId w, QWidget *parent, const char *name )
: Client( ws, w, parent, name, WResizeNoErase )
{
create_pixmaps();
QFont f = font();
f.setBold( TRUE );
setFont( f );
QGridLayout* g = new QGridLayout( this, 0, 0, 2 );
g->setRowStretch( 1, 10 );
g->addWidget( windowWrapper(), 1, 1 );
g->addColSpacing(0, 2);
g->addColSpacing(2, 2);
g->addRowSpacing(2, 2);
button[0] = new QToolButton( this );
button[1] = new QToolButton( this );
button[2] = new QToolButton( this );
button[3] = new QToolButton( this );
button[4] = new QToolButton( this );
button[5] = new QToolButton( this );
QHBoxLayout* hb = new QHBoxLayout;
g->addLayout( hb, 0, 1 );
hb->addWidget( button[0] );
hb->addWidget( button[1] );
hb->addWidget( button[2] );
int fh = fontMetrics().lineSpacing();
titlebar = new QSpacerItem(10, fh, QSizePolicy::Expanding,
QSizePolicy::Minimum );
hb->addItem( titlebar );
hb->addWidget( button[3] );
hb->addWidget( button[4] );
hb->addWidget( button[5] );
for ( int i = 0; i < 6; i++) {
button[i]->setMouseTracking( TRUE );
button[i]->setFixedSize( 20, 20 );
}
button[0]->setIconSet( miniIcon() );
button[1]->setIconSet( isSticky()?*pindown_pix:*pinup_pix );
connect( button[1], SIGNAL( clicked() ), this, ( SLOT( toggleSticky() ) ) );
button[2]->hide();
button[3]->setIconSet( *minimize_pix );
connect( button[3], SIGNAL( clicked() ), this, ( SLOT( iconify() ) ) );
button[4]->setIconSet( *maximize_pix );
connect( button[4], SIGNAL( clicked() ), this, ( SLOT( maximize() ) ) );
button[5]->setIconSet( *close_pix );
connect( button[5], SIGNAL( clicked() ), this, ( SLOT( closeWindow() ) ) );
}
StdClient::~StdClient()
{
}
void StdClient::resizeEvent( QResizeEvent* e)
{
Client::resizeEvent( e );
if ( isVisibleToTLW() ) {
// manual clearing without the titlebar (we selected WResizeNoErase )
QPainter p( this );
QRect t = titlebar->geometry();
t.setTop( 0 );
QRegion r = rect();
r = r.subtract( t );
p.setClipRegion( r );
p.eraseRect( rect() );
}
}
/*!\reimp
*/
void StdClient::captionChange( const QString& )
{
repaint( titlebar->geometry(), FALSE );
}
/*!\reimp
*/
void StdClient::maximizeChange( bool m )
{
button[4]->setIconSet( m?*normalize_pix:*maximize_pix );
}
/*!\reimp
*/
void StdClient::stickyChange( bool s)
{
button[1]->setIconSet( s?*pindown_pix:*pinup_pix );
}
void StdClient::paintEvent( QPaintEvent* )
{
QPainter p( this );
QRect t = titlebar->geometry();
t.setTop( 0 );
QRegion r = rect();
r = r.subtract( t );
p.setClipRegion( r );
qDrawWinPanel( &p, rect(), colorGroup() );
p.setClipping( FALSE );
p.fillRect( t, isActive()?darkBlue:gray );
qDrawShadePanel( &p, t.x(), t.y(), t.width(), t.height(),
colorGroup(), TRUE );
t.setTop( 2 );
t.setLeft( t.left() + 4 );
t.setRight( t.right() - 2 );
p.setPen( colorGroup().light() );
p.drawText( t, AlignLeft|AlignVCenter, caption() );
}
void StdClient::mouseDoubleClickEvent( QMouseEvent * e )
{
if ( titlebar->geometry().contains( e->pos() ) )
setShade( !isShade() );
workspace()->requestFocus( this );
}
void StdClient::init()
{
button[0]->setIconSet( miniIcon() );
// ### TODO transient etc.
}
void StdClient::iconChange()
{
button[0]->setIconSet( miniIcon() );
button[0]->repaint( FALSE );
}

33
stdclient.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef STDCLIENT_H
#define STDCLIENT_H
#include "client.h"
class QToolButton;
class QLabel;
class QSpacerItem;
class StdClient : public Client
{
Q_OBJECT
public:
StdClient( Workspace *ws, WId w, QWidget *parent=0, const char *name=0 );
~StdClient();
protected:
void resizeEvent( QResizeEvent* );
void paintEvent( QPaintEvent* );
void mouseDoubleClickEvent( QMouseEvent * );
void init();
void captionChange( const QString& name );
void iconChange();
void maximizeChange( bool );
void stickyChange( bool );
private:
QToolButton* button[6];
QSpacerItem* titlebar;
};
#endif

179
tabbox.cpp Normal file
View file

@ -0,0 +1,179 @@
#include "tabbox.h"
#include "workspace.h"
#include "client.h"
#include <qpainter.h>
const bool options_traverse_all = FALSE; // TODO
TabBox::TabBox( Workspace *ws, const char *name=0 )
: QWidget( 0, name, WStyle_Customize | WStyle_NoBorder )
{
wspace = ws;
reset();
}
TabBox::~TabBox()
{
}
/*!
Sets the current mode to \a mode, either DesktopMode or WindowsMode
\sa mode()
*/
void TabBox::setMode( Mode mode )
{
m = mode;
}
/*!
Resets the tab box to display the active client in WindowsMode, or the
current desktop in DesktopMode
*/
void TabBox::reset()
{
QFont f = font();
f.setBold( TRUE );
f.setPointSize( 14 );
setFont( f );
// TODO icons etc.
setGeometry( qApp->desktop()->width()/4,
qApp->desktop()->height()/2-fontMetrics().height()*2,
qApp->desktop()->width()/2, fontMetrics().height()*4 );
if ( mode() == WindowsMode ) {
client = workspace()->activeClient();
// todo build window list, consider options_traverse_all
}
else { // DesktopMode
desk = wspace->currentDesktop();
}
}
/*!
Shows the next or previous item, depending on \a next
*/
void TabBox::nextPrev( bool next)
{
if ( mode() == WindowsMode ) {
Client* sign = client;
do {
if (client != sign && !sign)
sign = client;
if ( next )
client = workspace()->nextClient(client);
else
client = workspace()->previousClient(client);
} while (client != sign && client &&
!options_traverse_all &&
!client->isOnDesktop(workspace()->currentDesktop()));
if (!options_traverse_all && client
&& !client->isOnDesktop(workspace()->currentDesktop()))
client = 0;
}
else { // DesktopMode
if ( next ) {
desk++;
if ( desk > wspace->numberOfDesktops() )
desk = 1;
} else {
desk--;
if ( desk < 1 )
desk = wspace->numberOfDesktops();
}
}
paintContents();
}
/*!
Returns the currently displayed client ( only works in WindowsMode ).
Returns 0 if no client is displayed.
*/
Client* TabBox::currentClient()
{
if ( mode() != WindowsMode )
return 0;
return client;
}
/*!
Returns the currently displayed virtual desktop ( only works in
DesktopMode )
Returns -1 if no desktop is displayed.
*/
int TabBox::currentDesktop()
{
if ( mode() != DesktopMode )
return -1;
return desk;
}
/*!
Reimplemented to raise the tab box as well
*/
void TabBox::showEvent( QShowEvent* )
{
raise();
}
/*!
Paints the tab box
*/
void TabBox::paintEvent( QPaintEvent* )
{
{
QPainter p( this );
style().drawPanel( &p, 0, 0, width(), height(), colorGroup(), FALSE );
style().drawPanel( &p, 4, 4, width()-8, height()-8, colorGroup(), TRUE );
}
paintContents();
}
/*!
Paints the contents of the tab box. Used in paintEvent() and
whenever the contents changes.
*/
void TabBox::paintContents()
{
QPainter p( this );
QRect r(6, 6, width()-12, height()-12 );
p.fillRect( r, colorGroup().brush( QColorGroup::Background ) );
if ( mode () == WindowsMode ) {
if ( currentClient() ) {
QString s;
if (!client->isOnDesktop(workspace()->currentDesktop())){
//TODO s = KWM::getDesktopName(client->desktop);
s.append(": ");
}
if (client->isIconified())
s += QString("(")+client->caption()+")";
else
s += client->caption();
if ( p.fontMetrics().width( s ) > r.width() )
p.drawText( r, AlignLeft, s );
else
p.drawText( r, AlignCenter, s );
}
else {
p.drawText( r, AlignCenter, "*** No Tasks ***" );
}
} else { // DesktopMode
QString s;
s.setNum( desk );
p.drawText( r, AlignCenter, s );
}
}

63
tabbox.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef TABBOX_H
#define TABBOX_H
#include <qwidget.h>
class Workspace;
class Client;
typedef QValueList<Client*> ClientList;
class TabBox : public QWidget
{
Q_OBJECT
public:
TabBox( Workspace *ws, const char *name=0 );
~TabBox();
Client* currentClient();
int currentDesktop();
enum Mode { DesktopMode, WindowsMode };
void setMode( Mode mode );
Mode mode() const;
void reset();
void nextPrev( bool next = TRUE);
Workspace* workspace() const;
protected:
void paintEvent( QPaintEvent* );
void showEvent( QShowEvent* );
void paintContents();
private:
Client* client;
Mode m;
Workspace* wspace;
ClientList clients;
int desk;
// QValueList <QLabel*> labels;
};
/*!
Returns the tab box' workspace
*/
inline Workspace* TabBox::workspace() const
{
return wspace;
}
/*!
Returns the current mode, either DesktopMode or WindowsMode
\sa setMode()
*/
inline TabBox::Mode TabBox::mode() const
{
return m;
}
#endif

859
workspace.cpp Normal file
View file

@ -0,0 +1,859 @@
#include "workspace.h"
#include "client.h"
#include "stdclient.h"
#include "beclient.h"
#include "tabbox.h"
#include "atoms.h"
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
static Client* clientFactory( Workspace *ws, WId w )
{
// hack TODO hints
char* name = 0;
QString s;
if ( XFetchName( qt_xdisplay(), (Window) w, &name ) && name ) {
s = QString::fromLatin1( name );
XFree( name );
}
if ( s == "desktop") {
Client * c = new NoBorderClient( ws, w);
ws->setDesktopClient( c );
return c;
}
return new StdClient( ws, w );
}
Workspace::Workspace()
{
root = qt_xrootwin(); // no MDI for now
(void) QApplication::desktop(); // trigger creation of desktop widget
// select windowmanager privileges
XSelectInput(qt_xdisplay(), root,
KeyPressMask |
PropertyChangeMask |
ColormapChangeMask |
SubstructureRedirectMask |
SubstructureNotifyMask
);
init();
control_grab = FALSE;
tab_grab = FALSE;
tab_box = new TabBox( this );
grabKey(XK_Tab, Mod1Mask);
grabKey(XK_Tab, Mod1Mask | ShiftMask);
grabKey(XK_Tab, ControlMask);
grabKey(XK_Tab, ControlMask | ShiftMask);
}
Workspace::Workspace( WId rootwin )
{
qDebug("create MDI workspace for %d", rootwin );
root = rootwin;
// select windowmanager privileges
XSelectInput(qt_xdisplay(), root,
KeyPressMask |
PropertyChangeMask |
ColormapChangeMask |
SubstructureRedirectMask |
SubstructureNotifyMask
);
init();
control_grab = FALSE;
tab_grab = FALSE;
tab_box = new TabBox( this );
grabKey(XK_Tab, Mod1Mask);
grabKey(XK_Tab, Mod1Mask | ShiftMask);
grabKey(XK_Tab, ControlMask);
grabKey(XK_Tab, ControlMask | ShiftMask);
}
void Workspace::init()
{
tab_box = 0;
active_client = 0;
should_get_focus = 0;
desktop_client = 0;
current_desktop = 1;
unsigned int i, nwins;
Window dw1, dw2, *wins;
XWindowAttributes attr;
XGrabServer( qt_xdisplay() );
XQueryTree(qt_xdisplay(), root, &dw1, &dw2, &wins, &nwins);
for (i = 0; i < nwins; i++) {
XGetWindowAttributes(qt_xdisplay(), wins[i], &attr);
if (attr.override_redirect )
continue;
if (attr.map_state != IsUnmapped) {
Client* c = clientFactory( this, wins[i] );
clients.append( c );
if ( c != desktop_client )
stacking_order.append( c );
focus_chain.append( c );
c->manage( TRUE );
if ( c == desktop_client )
setDesktopClient( c );
if ( root != qt_xrootwin() ) {
// TODO may use QWidget:.create
qDebug(" create a mdi client");
XReparentWindow( qt_xdisplay(), c->winId(), root, 0, 0 );
c->move(0,0);
}
}
}
XFree((void *) wins);
XUngrabServer( qt_xdisplay() );
popup = 0;
}
Workspace::~Workspace()
{
for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) {
delete (*it);
}
delete tab_box;
delete popup;
}
/*!
Handles workspace specific XEvents
*/
bool Workspace::workspaceEvent( XEvent * e )
{
Client * c = findClient( e->xany.window );
if ( c )
return c->windowEvent( e );
switch (e->type) {
case ButtonPress:
case ButtonRelease:
break;
case UnmapNotify:
// this is special due to
// SubstructureRedirectMask. e->xany.window is the window the
// event is reported to. Take care not to confuse Qt.
c = findClient( e->xunmap.window );
if ( c )
return c->windowEvent( e );
if ( e->xunmap.event != e->xunmap.window ) // hide wm typical event from Qt
return TRUE;
case ReparentNotify:
//do not confuse Qt with these events. After all, _we_ are the
//window manager who does the reparenting.
return true;
case DestroyNotify:
return destroyClient( findClient( e->xdestroywindow.window ) );
case MapRequest:
if ( e->xmaprequest.parent == root ) {
c = findClient( e->xmaprequest.window );
if ( !c ) {
c = clientFactory( this, e->xmaprequest.window );
if ( root != qt_xrootwin() ) {
// TODO may use QWidget:.create
XReparentWindow( qt_xdisplay(), c->winId(), root, 0, 0 );
}
clients.append( c );
if ( c != desktop_client )
stacking_order.append( c );
}
bool result = c->windowEvent( e );
if ( c == desktop_client )
setDesktopClient( c );
return result;
}
break;
case ConfigureRequest:
if ( e->xconfigurerequest.parent == root ) {
XWindowChanges wc;
unsigned int value_mask = 0;
wc.border_width = 0;
wc.x = e->xconfigurerequest.x;
wc.y = e->xconfigurerequest.y;
wc.width = e->xconfigurerequest.width;
wc.height = e->xconfigurerequest.height;
wc.sibling = None;
wc.stack_mode = Above;
value_mask = e->xconfigurerequest.value_mask | CWBorderWidth;
XConfigureWindow( qt_xdisplay(), e->xconfigurerequest.window, value_mask, & wc );
XWindowAttributes attr;
if (XGetWindowAttributes(qt_xdisplay(), e->xconfigurerequest.window, &attr)){
// send a synthetic configure notify in any case (even if we didn't change anything)
XConfigureEvent c;
c.type = ConfigureNotify;
c.event = e->xconfigurerequest.window;
c.window = e->xconfigurerequest.window;
c.x = attr.x;
c.y = attr.y;
c.width = attr.width;
c.height = attr.height;
c.border_width = 0;
XSendEvent( qt_xdisplay(), c.event, TRUE, NoEventMask, (XEvent*)&c );
}
return TRUE;
}
else {
c = findClient( e->xconfigurerequest.window );
if ( c )
return c->windowEvent( e );
}
break;
case KeyPress:
return keyPress(e->xkey);
break;
case KeyRelease:
return keyRelease(e->xkey);
break;
case FocusIn:
break;
case FocusOut:
break;
default:
break;
}
return FALSE;
}
/*!
Finds the client that embedds the window \a w
*/
Client* Workspace::findClient( WId w ) const
{
for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) {
if ( (*it)->window() == w )
return *it;
}
return 0;
}
/*!
Returns the workspace's geometry
*/
QRect Workspace::geometry() const
{
if ( root == qt_xrootwin() )
return QRect( QPoint(0, 0), QApplication::desktop()->size() );
else {
// todo caching, keep track of configure notify etc.
QRect r;
XWindowAttributes attr;
if (XGetWindowAttributes(qt_xdisplay(), root, &attr)){
r.setRect(0, 0, attr.width, attr.height );
}
return r;
}
}
/*
Destroys the client \a c
*/
bool Workspace::destroyClient( Client* c)
{
if ( !c )
return FALSE;
clients.remove( c );
stacking_order.remove( c );
focus_chain.remove( c );
c->invalidateWindow();
delete c;
clientHidden( c );
return TRUE;
}
/*!
Auxiliary function to release a passive keyboard grab
*/
void Workspace::freeKeyboard(bool pass){
if (!pass)
XAllowEvents(qt_xdisplay(), AsyncKeyboard, CurrentTime);
else
XAllowEvents(qt_xdisplay(), ReplayKeyboard, CurrentTime);
QApplication::syncX();
}
/*!
Handles alt-tab / control-tab
*/
bool Workspace::keyPress(XKeyEvent key)
{
if ( root != qt_xrootwin() )
return FALSE;
int kc = XKeycodeToKeysym(qt_xdisplay(), key.keycode, 0);
int km = key.state & (ControlMask | Mod1Mask | ShiftMask);
const bool options_alt_tab_mode_is_CDE_style = FALSE; // TODO
if (!control_grab){
if( (kc == XK_Tab) &&
( km == (Mod1Mask | ShiftMask)
|| km == (Mod1Mask)
)){
if (!tab_grab){
if (options_alt_tab_mode_is_CDE_style ){
// CDE style raise / lower
Client* c = topClientOnDesktop();
Client* nc = c;
if (km & ShiftMask){
do {
nc = previousStaticClient(nc);
} while (nc && nc != c &&
(!nc->isOnDesktop(currentDesktop()) ||
nc->isIconified()));
}
else
do {
nc = nextStaticClient(nc);
} while (nc && nc != c &&
(!nc->isOnDesktop(currentDesktop()) ||
nc->isIconified()));
if (c && c != nc)
;//TODO lowerClient(c);
if (nc)
activateClient( nc );
freeKeyboard(FALSE);
return TRUE;
}
XGrabKeyboard(qt_xdisplay(),
root, FALSE,
GrabModeAsync, GrabModeAsync,
CurrentTime);
tab_grab = TRUE;
tab_box->setMode( TabBox::WindowsMode );
tab_box->reset();
}
tab_box->nextPrev( (km & ShiftMask) == 0 );
tab_box->show();
}
}
if (!tab_grab){
if( (kc == XK_Tab) &&
( km == (ControlMask | ShiftMask)
|| km == (ControlMask)
)){
//TODO if (!options.ControlTab){
// freeKeyboard(TRUE);
// return TRUE;
// }
if (!control_grab){
XGrabKeyboard(qt_xdisplay(),
root, FALSE,
GrabModeAsync, GrabModeAsync,
CurrentTime);
control_grab = TRUE;
tab_box->setMode( TabBox::DesktopMode );
tab_box->reset();
}
tab_box->nextPrev( (km & ShiftMask) == 0 );
tab_box->show();
}
}
if (control_grab || tab_grab){
if (kc == XK_Escape){
XUngrabKeyboard(qt_xdisplay(), CurrentTime);
tab_box->hide();
tab_grab = FALSE;
control_grab = FALSE;
return TRUE;
}
return FALSE;
}
freeKeyboard(FALSE);
return FALSE;
}
/*!
Handles alt-tab / control-tab
*/
bool Workspace::keyRelease(XKeyEvent key)
{
if ( root != qt_xrootwin() )
return FALSE;
int i;
if (tab_grab){
XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay());
for (i=0; i<xmk->max_keypermod; i++)
if (xmk->modifiermap[xmk->max_keypermod * Mod1MapIndex + i]
== key.keycode){
XUngrabKeyboard(qt_xdisplay(), CurrentTime);
tab_box->hide();
tab_grab = false;
if ( tab_box->currentClient() ){
activateClient( tab_box->currentClient() );
}
}
}
if (control_grab){
XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay());
for (i=0; i<xmk->max_keypermod; i++)
if (xmk->modifiermap[xmk->max_keypermod * ControlMapIndex + i]
== key.keycode){
XUngrabKeyboard(qt_xdisplay(), CurrentTime);
tab_box->hide();
control_grab = False;
if ( tab_box->currentDesktop() != -1 )
switchDesktop( tab_box->currentDesktop() );
}
}
return FALSE;
}
/*!
auxiliary functions to travers all clients according the focus
order. Useful for kwm´s Alt-tab feature.
*/
Client* Workspace::nextClient( Client* c ) const
{
if ( focus_chain.isEmpty() )
return 0;
ClientList::ConstIterator it = focus_chain.find( c );
if ( it == focus_chain.end() )
return focus_chain.last();
if ( it == focus_chain.begin() )
return focus_chain.last();
--it;
return *it;
}
/*!
auxiliary functions to travers all clients according the focus
order. Useful for kwm´s Alt-tab feature.
*/
Client* Workspace::previousClient( Client* c ) const
{
if ( focus_chain.isEmpty() )
return 0;
ClientList::ConstIterator it = focus_chain.find( c );
if ( it == focus_chain.end() )
return focus_chain.first();
++it;
if ( it == focus_chain.end() )
return focus_chain.first();
return *it;
}
/*!
auxiliary functions to travers all clients according the static
order. Useful for the CDE-style Alt-tab feature.
*/
Client* Workspace::nextStaticClient( Client* c ) const
{
if ( clients.isEmpty() )
return 0;
ClientList::ConstIterator it = clients.find( c );
if ( it == clients.end() )
return clients.first();
++it;
if ( it == clients.end() )
return clients.first();
return *it;
}
/*!
auxiliary functions to travers all clients according the static
order. Useful for the CDE-style Alt-tab feature.
*/
Client* Workspace::previousStaticClient( Client* c ) const
{
if ( clients.isEmpty() )
return 0;
ClientList::ConstIterator it = clients.find( c );
if ( it == clients.end() )
return clients.last();
if ( it == clients.begin() )
return clients.last();
--it;
return *it;
}
/*!
Returns topmost visible client within the specified layer range on
the current desktop, or 0 if no clients are visible. \a fromLayer has to
be smaller than \a toLayer.
*/
Client* Workspace::topClientOnDesktop( int fromLayer, int toLayer) const
{
fromLayer = toLayer = 0;
return 0;
}
/*
Grabs the keysymbol \a keysym with the given modifiers \a mod
plus all possibile combinations of Lock and NumLock
*/
void Workspace::grabKey(KeySym keysym, unsigned int mod){
static int NumLockMask = 0;
if (!keysym||!XKeysymToKeycode(qt_xdisplay(), keysym)) return;
if (!NumLockMask){
XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay());
int i;
for (i=0; i<8; i++){
if (xmk->modifiermap[xmk->max_keypermod * i] ==
XKeysymToKeycode(qt_xdisplay(), XK_Num_Lock))
NumLockMask = (1<<i);
}
}
XGrabKey(qt_xdisplay(),
XKeysymToKeycode(qt_xdisplay(), keysym), mod,
qt_xrootwin(), TRUE,
GrabModeSync, GrabModeSync);
XGrabKey(qt_xdisplay(),
XKeysymToKeycode(qt_xdisplay(), keysym), mod | LockMask,
qt_xrootwin(), TRUE,
GrabModeSync, GrabModeSync);
XGrabKey(qt_xdisplay(),
XKeysymToKeycode(qt_xdisplay(), keysym), mod | NumLockMask,
qt_xrootwin(), TRUE,
GrabModeSync, GrabModeSync);
XGrabKey(qt_xdisplay(),
XKeysymToKeycode(qt_xdisplay(), keysym), mod | LockMask | NumLockMask,
qt_xrootwin(), TRUE,
GrabModeSync, GrabModeSync);
}
/*!
Informs the workspace about the active client, i.e. the client that
has the focus (or None if no client has the focus). This functions
is called by the client itself that gets focus. It has no other
effect than fixing the focus chain and the return value of activeClient()
*/
void Workspace::setActiveClient( Client* c )
{
if ( active_client == c )
return;
if ( active_client )
active_client->setActive( FALSE );
active_client = c;
if ( active_client ) {
focus_chain.remove( c );
focus_chain.append( c );
}
}
/*!
Tries to activate the client \a c. This function performs what you
expect when clicking the respective entry in a taskbar: showing and
raising the client (this may imply switching to the another virtual
desktop) and putting the focus onto it. Once X really gave focus to
the client window as requested, the client itself will call
setActiveClient() and the operation is complete. This may not happen
with certain focus policies, though.
\sa setActiveClient(), requestFocus()
*/
void Workspace::activateClient( Client* c)
{
if (!c->isOnDesktop(currentDesktop()) ) {
// TODO switch desktop
}
raiseClient( c );
c->show();
if ( options->focusPolicyIsReasonable() )
requestFocus( c );
}
/*!
Tries to activate the client by asking X for the input focus. This
function does not perform any show, raise or desktop switching. See
Workspace::activateClient() instead.
\sa Workspace::activateClient()
*/
void Workspace::requestFocus( Client* c)
{
//TODO will be different for non-root clients. (subclassing?)
if ( !c ) {
focusToNull();
return;
}
if ( c->isVisible() && !c->isShade() ) {
c->takeFocus();
should_get_focus = c;
} else if ( c->isShade() ) {
// client cannot accept focus, but at least the window should be active (window menu, et. al. )
focusToNull();
c->setActive( TRUE );
}
}
/*!
Informs the workspace that the client \a c has been hidden. If it
was the active client, the workspace activates another one.
\a c may already be destroyed
*/
void Workspace::clientHidden( Client* c )
{
if ( c == active_client || ( !active_client && c == should_get_focus ) ) {
active_client = 0;
should_get_focus = 0;
if ( clients.contains( c ) ) {
focus_chain.remove( c );
focus_chain.prepend( c );
}
if ( options->focusPolicyIsReasonable() ) {
for ( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.begin(); --it) {
if ( (*it)->isVisible() ) {
requestFocus( *it );
break;
}
}
}
}
}
void Workspace::showPopup( const QPoint& pos, Client* c)
{
// experimental!!!
if ( !popup ) {
popup = new QPopupMenu;
// I wish I could use qt-2.1 features here..... grmblll
QPopupMenu* deco = new QPopupMenu( popup );
deco->insertItem("KDE Classic", 100 );
deco->insertItem("Be-like style", 101 );
popup->insertItem("Decoration", deco );
}
popup_client = c;
// TODO customize popup for the client
int ret = popup->exec( pos );
switch( ret ) {
case 100:
setDecoration( 0 );
break;
case 101:
setDecoration( 1 );
break;
default:
break;
}
popup_client = 0;
ret = 0;
}
/*!
Places the client \a c according to the workspace's layout policy
*/
void Workspace::doPlacement( Client* c )
{
randomPlacement( c );
}
/*!
Place the client \a c according to a simply "random" placement algorithm.
*/
void Workspace::randomPlacement(Client* c){
const int step = 24;
static int px = step;
static int py = 2 * step;
int tx,ty;
QRect maxRect = geometry(); // TODO
if (px < maxRect.x())
px = maxRect.x();
if (py < maxRect.y())
py = maxRect.y();
px += step;
py += 2*step;
if (px > maxRect.width()/2)
px = maxRect.x() + step;
if (py > maxRect.height()/2)
py = maxRect.y() + step;
tx = px;
ty = py;
if (tx + c->width() > maxRect.right()){
tx = maxRect.right() - c->width();
if (tx < 0)
tx = 0;
px = maxRect.x();
}
if (ty + c->height() > maxRect.bottom()){
ty = maxRect.bottom() - c->height();
if (ty < 0)
ty = 0;
py = maxRect.y();
}
c->move( tx, ty );
}
/*!
Raises the client \a c taking layers, transient windows and window
groups into account.
*/
void Workspace::raiseClient( Client* c )
{
if ( !c )
return;
if ( c == desktop_client )
return; // deny
Window* new_stack = new Window[ stacking_order.count()+1];
stacking_order.remove( c );
stacking_order.append( c );
ClientList saveset;
saveset.append( c );
raiseTransientsOf(saveset, c );
int i = 0;
for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) {
new_stack[i++] = (*it)->winId();
}
XRaiseWindow(qt_xdisplay(), new_stack[0]);
XRestackWindows(qt_xdisplay(), new_stack, i);
delete [] new_stack;
if ( c->transientFor() )
raiseClient( findClient( c->transientFor() ) );
}
/*!
Private auxiliary function used in raiseClient()
*/
void Workspace::raiseTransientsOf( ClientList& safeset, Client* c )
{
ClientList local = stacking_order;
for ( ClientList::ConstIterator it = local.begin(); it != local.end(); ++it) {
if ( (*it)->transientFor() == c->window() && !safeset.contains( *it ) ) {
safeset.append( *it );
stacking_order.remove( *it );
stacking_order.append( *it );
raiseTransientsOf( safeset, *it );
}
}
}
/*!
Puts the focus on a dummy winodw
*/
void Workspace::focusToNull(){
static Window w = 0;
int mask;
XSetWindowAttributes attr;
if (w == 0) {
mask = CWOverrideRedirect;
attr.override_redirect = 1;
w = XCreateWindow(qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, CopyFromParent,
InputOnly, CopyFromParent, mask, &attr);
XMapWindow(qt_xdisplay(), w);
}
XSetInputFocus(qt_xdisplay(), w, RevertToPointerRoot, CurrentTime );
//colormapFocus(0); TODO
}
void Workspace::setDesktopClient( Client* c)
{
desktop_client = c;
if ( desktop_client ) {
desktop_client->lower();
desktop_client->setGeometry( geometry() );
}
}
void Workspace::switchDesktop( int new_desktop ){
if (new_desktop == current_desktop )
return;
/*
optimized Desktop switching: unmapping done from back to front
mapping done from front to back => less exposure events
*/
for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) {
if ( (*it)->isVisible() && !(*it)->isOnDesktop( new_desktop ) ) {
(*it)->hide();
}
}
for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) {
if ( (*it)->isOnDesktop( new_desktop ) ) {
(*it)->show();
//XMapWindow( qt_xdisplay(), (*it)->winId() );
}
}
current_desktop = new_desktop;
}
void Workspace::makeFullScreen( Client* )
{
// not yet implemented
}
// experimental
void Workspace::setDecoration( int deco )
{
if ( !popup_client )
return;
Client* c = popup_client;
WId w = c->window();
clients.remove( c );
stacking_order.remove( c );
focus_chain.remove( c );
bool mapped = c->isVisible();
c->hide();
c->releaseWindow();
switch ( deco ) {
case 1:
c = new BeClient( this, w);
break;
default:
c = new StdClient( this, w );
}
clients.append( c );
stacking_order.append( c );
c->manage( mapped );
activateClient( c );
}

126
workspace.h Normal file
View file

@ -0,0 +1,126 @@
#ifndef WORKSPACE_H
#define WORKSPACE_H
#include <qwidget.h>
#include <qapplication.h>
#include <qpopupmenu.h>
#include <qvaluelist.h>
#include <X11/Xlib.h>
class Client;
class TabBox;
typedef QValueList<Client*> ClientList;
class Workspace : public QObject
{
Q_OBJECT
public:
Workspace();
Workspace( WId rootwin );
virtual ~Workspace();
virtual bool workspaceEvent( XEvent * );
Client* findClient( WId w ) const;
QRect geometry() const;
bool destroyClient( Client* );
WId rootWin() const;
Client* activeClient() const;
void setActiveClient( Client* );
void activateClient( Client* );
void requestFocus( Client* c);
void doPlacement( Client* c );
void raiseClient( Client* c );
void clientHidden( Client* );
int currentDesktop() const;
int numberOfDesktops() const;
void grabKey(KeySym keysym, unsigned int mod);
Client* nextClient(Client*) const;
Client* previousClient(Client*) const;
Client* nextStaticClient(Client*) const;
Client* previousStaticClient(Client*) const;
//#### TODO right layers as default
Client* topClientOnDesktop( int fromLayer = 0, int toLayer = 0) const;
void showPopup( const QPoint&, Client* );
void setDesktopClient( Client* );
void switchDesktop( int new_desktop );
void makeFullScreen( Client* );
protected:
bool keyPress( XKeyEvent key );
bool keyRelease( XKeyEvent key );
private:
void init();
WId root;
ClientList clients;
ClientList stacking_order;
ClientList focus_chain;
Client* active_client;
bool control_grab;
bool tab_grab;
TabBox* tab_box;
void freeKeyboard(bool pass);
QPopupMenu *popup;
Client* should_get_focus;
void raiseTransientsOf( ClientList& safeset, Client* c );
void randomPlacement(Client* c);
void focusToNull();
Client* desktop_client;
int current_desktop;
Client* popup_client;
//experimental
void setDecoration( int deco );
};
inline WId Workspace::rootWin() const
{
return root;
}
/*!
Returns the active client, i.e. the client that has the focus (or None if no
client has the focus)
*/
inline Client* Workspace::activeClient() const
{
return active_client;
}
/*!
Returns the current virtual desktop of this workspace
*/
inline int Workspace::currentDesktop() const
{
return current_desktop;
}
/*!
Returns the number of virtual desktops of this workspace
*/
inline int Workspace::numberOfDesktops() const
{
return 4;
}
#endif