/* Clementine Window Manager
Copyright 2002 Dave Berton <db@mosey.org>
based on aewm Copyright 1998-2001 Decklin Foster <decklin@red-bean.com>
This program is free software; see LICENSE for details. */
#include "client.h"
#include "windowsystem.h"
#include "painter.h"
#include <iostream>
#include <assert.h>
#include <iostream> // ## db more
#define PIXELS 0
#define INCREMENTS 1
Client::Client( WindowSystem* windowsystem, Window w, bool viewable,
int wx, int wy, int wwidth, int wheight,
long wcolormap )
: ws(windowsystem)
{
assert(ws);
ws->transientForHint( w, &trans );
nm = ws->windowName( w );
window = w;
ignore_unmap = 0;
x = wx;
y = wy;
width = wwidth;
height = wheight;
cmap = wcolormap;
size = ws->allocSizeHints();
active = false;
awaiting_close = false;
awaiting_iconize = false;
long dummy;
ws->windowNormalHints( window, size, &dummy );
if ( viewable ) {
ignore_unmap++;
} else {
initPosition();
ws->setState(window, NormalState);
// consolidate
XWMHints *hints;
if ((hints = ws->windowHints(w))) {
if (hints->flags & StateHint)
ws->setState(window, hints->initial_state);
ws->xFree(hints);
}
}
gravitate( APPLY_GRAVITY );
frame = ws->createFrame( x-1, y - titleHeight(),
width + 2, // 1 pixel border, each side
height + titleHeight() + 1 ); // 1 pixel border, top
button_size = titleHeight() - 2;
titlebar_width = width - 2*button_size;
close_button = ws->createWindow( 0,0, button_size, button_size );
iconize_button = ws->createWindow( 0,0, button_size, button_size );
titlebar = ws->createWindow( 0,0, titlebar_width, button_size );
ws->addToSaveSet( window );
ws->selectInput( window, ColormapChangeMask|PropertyChangeMask);
ws->grabButtons( window );
ws->setBorderWidth( window, 0);
ws->resize( window, width, height); //db more needed?
ws->reparent( close_button, frame, width + 1 - button_size, 1 );
ws->reparent( iconize_button, frame, 1, 1 );
ws->reparent( titlebar, frame, button_size + 1, 1 );
ws->reparent( window, frame, 1, titleHeight()); // for 1 pixel border
configure();
// db more fix: initial iconized windows
if ( viewable ) {
if ( ws->getState(window) == IconicState ) {
ignore_unmap++;
ws->unmap( window );
} else {
mapRaised();
}
} else {
if ( ws->getState(window) == NormalState ) {
ws->map( window );
ws->map( close_button );
ws->map( iconize_button );
ws->map( titlebar );
ws->mapRaised( frame );
}
}
redraw( false, true );
}
Client::~Client()
{
// if ( ignore_unmap )
// throw ( "Client withdrawing directly!" ); // ## db more
ws->setState( window, WithdrawnState );
gravitate( REMOVE_GRAVITY );
ws->setBorderWidth( window, 1 );
ws->removeFromSaveSet( window );
ws->reparentToRoot( window, x, y);
ws->destroy( close_button );
ws->destroy( iconize_button );
ws->destroy( titlebar );
ws->destroy( frame );
if (size)
ws->xFree(size);
}
int Client::titleHeight() const
{
return ws->painter()->fontAscent() +
ws->painter()->fontDescent() +2*ws->pad();
return ( trans ? 3 :
ws->painter()->fontAscent() +
ws->painter()->fontDescent() +2*ws->pad());
}
/* Window gravity is a mess to explain, but we don't need to do much
* about it since we're using X borders. For NorthWest et al, the top
* left corner of the window when there is no WM needs to match up
* with the top left of our fram once we manage it, and likewise with
* SouthWest and the bottom right (these are the only values I ever
* use, but the others should be obvious.) Our titlebar is on the top
* so we only have to adjust in the first case. */
void Client::gravitate( Client::GravityType type )
{
int dy = 0;
int gravity = (size->flags & PWinGravity) ?
size->win_gravity : NorthWestGravity;
switch (gravity) {
case NorthWestGravity:
case NorthEastGravity:
case NorthGravity:
dy = titleHeight();
break;
case CenterGravity:
dy = titleHeight()/2;
break;
}
y += type * dy;
}
/* Figure out where to map the window. c->x, c->y, c->width, and
* c->height actually start out with values in them (whatever the
* client passed to XCreateWindow). Program-specified hints will
* override these, but anything set by the program will be
* sanity-checked before it is used. PSize is ignored completely,
* because GTK sets it to 200x200 for almost everything. User-
* specified hints will of course override anything the program says.
*
* If we can't find a reasonable position hint, we make up a position
* using the mouse co-ordinates and window size. If the mouse is in
* the center, we center the window; if it's at an edge, the window
* goes on the edge. To account for window gravity while doing this,
* we add theight into the calculation and then degravitate. Don't
* think about it too hard, or your head will explode.
*
* If we are using the gnome_pda hint, the entire process is done over
* a smaller "pretend" root window, and then at the very end we shift
* the window into the right place based using the left/top offsets. */
void Client::initPosition()
{
int xmax = ws->displayWidth();
int ymax = ws->displayHeight();;
if (size->flags & (USSize)) {
if (size->width) width = size->width;
if (size->height) height = size->height;
} else {
/* make sure it's big enough to click at */
if (width < 2 * titleHeight()) width = 2 * titleHeight();
if (height < titleHeight()) height = titleHeight();
}
if (size->flags & USPosition) {
x = size->x;
y = size->y;
} else {
if (size->flags & PPosition) {
x = size->x;
y = size->y;
}
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x > xmax) x = xmax - titleHeight();
if (y > ymax) y = ymax - titleHeight();
if (x == 0 && y == 0) {
int mouse_x, mouse_y;
ws->pointerPosition(&mouse_x, &mouse_y);
if (width < xmax)
x = int( (mouse_x < xmax ?
(mouse_x / (float)xmax) : 1)
* (xmax - width -
2 ) );
if (height + titleHeight() < ymax)
y = int( (mouse_y < ymax ?
(mouse_y / (float)ymax) : 1)
* (ymax - height - titleHeight() -
2) );
y += titleHeight();
gravitate( REMOVE_GRAVITY );
}
}
}
void Client::iconize()
{
// if ( !ignore_unmap ) // db more this used to be required, but seems to not be anymore. hmm.
// ignore_unmap++;
ws->unmap( frame );
ws->unmap( close_button );
ws->unmap( iconize_button );
ws->unmap( titlebar );
ws->unmap( window );
ws->iconize( window );
}
void Client::uniconize()
{
mapRaised();
}
void Client::map()
{
ws->map( window );
ws->map( close_button );
ws->map( iconize_button );
ws->map( titlebar );
}
void Client::mapRaised()
{
map();
ws->mapRaised( frame );
ws->setState( window, NormalState );
}
void Client::redraw( bool act, bool force )
{
bool cleared = false;
if ( force || act != active ) {
active = act;
ws->painter()->setBorderBackground( frame, active,
width + 2,
height + titleHeight() + 1,
titleHeight() );
ws->painter()->setTitlebarBackground( titlebar, active,
titlebar_width,
button_size);
ws->painter()->setButtonBackground( close_button,
Painter::CLOSE_BUTTON,
active,
false,
button_size,
button_size );
ws->painter()->setButtonBackground( iconize_button,
Painter::ICONIZE_BUTTON,
active,
false,
button_size,
button_size );
cleared = true;
}
if (!trans && name().length()) {
if ( !cleared )
ws->painter()->clear( titlebar );
ws->painter()->drawString( titlebar,
active,
ws->pad(),
ws->painter()->fontAscent() + ws->pad(),
name());
}
ws->sync();
}
void Client::takeFocus()
{
ws->setInputFocus( window );
ws->installColormap( cmap );
}
void Client::installColormap( long colormap )
{
cmap = colormap;
ws->installColormap( cmap );
}
long Client::state()
{
return ws->getState( window );
}
void Client::move()
{
ws->move( frame, x, y - titleHeight() );
configure();
}
void Client::moveResize()
{
titlebar_width = width - 2*button_size;
ws->moveResize( frame, x-1,
y - titleHeight(),
width + 2, height + titleHeight() + 1);
ws->moveResize( close_button, width + 1 - button_size, 1,
button_size, button_size);
ws->moveResize( iconize_button, 1, 1,
button_size, button_size);
ws->moveResize( titlebar, button_size + 1, 1,
titlebar_width, button_size);
ws->moveResize( window, 1, titleHeight(),
width, height);
configure();
}
void Client::configure()
{
ws->sendConfigureEvent( window, x, y,
width, height );
// db more needed?
// ws->sendConfigureEvent( close_button, width + 1 - button_size, 1,
// button_size, button_size );
// ws->sendConfigureEvent( iconize_button_button, 1, 1,
// button_size, button_size );
}
void Client::raise()
{
ws->raise( frame );
}
void Client::lower()
{
ws->lower( frame );
}
void Client::takeConfigureRequest( int x1, int y1,
unsigned int width1, unsigned int height1,
unsigned int valuemask,
Window sibling, int stack_mode )
{
gravitate( REMOVE_GRAVITY );
if (valuemask & CWX) x = x1;
if (valuemask & CWY) y = y1;
if (valuemask & CWWidth) width = width1;
if (valuemask & CWHeight) height = height1;
gravitate( APPLY_GRAVITY );
/* configure the frame */
ws->configure( frame, x, y - titleHeight(),
width, height + titleHeight(),
valuemask, sibling, stack_mode );
configure();
}
void Client::drag()
{
XEvent ev;
int x1, y1;
int old_cx = x;
int old_cy = y;
if (!ws->grabPointer(ws->moveCursor()))
return;
ws->pointerPosition(&x1, &y1);
ws->grabServer();
bool motion = false;
for (;;) {
ws->waitForMouse( &ev );
switch (ev.type) {
case MotionNotify:
if ( !motion ) {
drawOutline();
motion = true;
}
drawOutline(); /* clear */
x = old_cx + (ev.xmotion.x_root - x1);
y = old_cy + (ev.xmotion.y_root - y1);
drawOutline(); /* redraw */
break;
case ButtonRelease:
if ( motion )
drawOutline(); /* clear */
ws->ungrabServer();
ws->ungrabPointer();
move();
return; /* get out of loop */
}
}
}
void Client::drawOutline()
{
ws->painter()->rubberBand( x-1,
y - titleHeight(),
width+1,
height + titleHeight() );
}
void Client::resize()
{
XEvent ev;
int old_cx = x;
int old_cy = y;
if (!ws->grabPointer(ws->resizeCursor()))
return;
ws->grabServer();
int pointer_x;
int pointer_y;
ws->pointerPosition(&pointer_x, &pointer_y);
int offset_x = x + width - pointer_x;
int offset_y = y + height - pointer_y;
drawOutline();
for (;;) {
ws->waitForMouse( &ev );
switch (ev.type) {
case MotionNotify:
drawOutline(); /* clear */
x = ev.xmotion.x_root + offset_x;
y = ev.xmotion.y_root + offset_y;
recalcResize(old_cx, old_cy,
x, y );
drawOutline();
break;
case ButtonRelease:
drawOutline(); /* clear */
moveResize();
redraw( active, true );
ws->ungrabServer();
ws->ungrabPointer();
return; /* get out of loop */
}
}
}
void Client::recalcResize(int x1, int y1, int x2, int y2)
{
width = abs(x1 - x2) - 2;
height = abs(y1 - y2) - 2;
getIncSize( &width, &height, PIXELS);
if (size->flags & PMinSize) {
if (width < size->min_width)
width = size->min_width;
if (height < size->min_height)
height = size->min_height;
}
if (size->flags & PMaxSize) {
if (width > size->max_width)
width = size->max_width;
if (height > size->max_height)
height = size->max_height;
}
x = (x1 <= x2) ? x1 : x1 - width;
y = (y1 <= y2) ? y1 : y1 - height;
}
/* If the window in question has a ResizeInc int, then it wants to be
* resized in multiples of some (x,y). Here we set x_ret and y_ret to
* the number of multiples (if mode == INCREMENTS) or the correct size
* in pixels for said multiples (if mode == PIXELS). */
int Client::getIncSize( int *x_ret, int *y_ret, int mode)
{
int basex, basey;
if (size->flags & PResizeInc) {
basex = (size->flags & PBaseSize) ? size->base_width :
(size->flags & PMinSize) ? size->min_width : 0;
basey = (size->flags & PBaseSize) ? size->base_height :
(size->flags & PMinSize) ? size->min_height : 0;
if (mode == PIXELS) {
*x_ret = width - ((width - basex) % size->width_inc);
*y_ret = height - ((height - basey) % size->height_inc);
} else /* INCREMENTS */ {
*x_ret = (width - basex) / size->width_inc;
*y_ret = (height - basey) / size->height_inc;
}
return 1;
}
return 0;
}
bool Client::checkUnmap()
{
if ( ignore_unmap ) {
ignore_unmap--;
return false;
} else {
return true;
}
}
void Client::takeButtonPress( Window w, unsigned int button, bool mod1,
int x, int y)
{
if ( w == close_button ) {
awaiting_close = true;
ws->painter()->setButtonBackground( close_button,
Painter::CLOSE_BUTTON,
active,
true,
button_size,
button_size );
}
if ( w == iconize_button ) {
awaiting_iconize = true;
ws->painter()->setButtonBackground( iconize_button,
Painter::ICONIZE_BUTTON,
active,
true,
button_size,
button_size );
}
if ( w == titlebar || w == frame ) {
raise();
redraw( true, true );
if ( button == 1 && !awaiting_close && !awaiting_iconize )
drag();
} else if ( isWindow( w ) ) {
if ( mod1 ) { /* mod click */
switch (button) {
case 1:
raise();
redraw( true, true );
drag();
break;
case 2:
break;
case 3:
raise();
redraw( true, true );
resize();
break;
}
}
}
}
void Client::takeButtonRelease( Window w, unsigned int button, bool mod1,
int x, int y )
{
if ( w == close_button ) {
if ( awaiting_close &&
( x > 0 && y > 0 && x < button_size && y < button_size )) {
ws->tryDelete( window );
}
ws->painter()->setButtonBackground( close_button,
Painter::CLOSE_BUTTON,
active,
false,
button_size,
button_size );
awaiting_close = false;
}
if ( w == iconize_button ) {
if ( awaiting_iconize &&
( x > 0 && y > 0 && x < button_size && y < button_size )) {
iconize();
}
ws->painter()->setButtonBackground( iconize_button,
Painter::ICONIZE_BUTTON,
active,
false,
button_size,
button_size );
awaiting_iconize = false;
}
if ( isFrame( w ) ) {
switch (button) {
case 1:
break;
case 2:
break;
case 3:
break;
}
} else {
awaiting_close = false;
awaiting_iconize = false;
}
}
syntax highlighted by Code2HTML, v. 0.9.1