/* glrectangle.hpp: Handles creating hardware accelerated rectangles on the screen using X11 and OpenGL.
*
* Copyright (C) 2014: Dalton Nell, Slop Contributors (https://github.com/naelstrof/slop/graphs/contributors).
*
* This file is part of Slop.
*
* Slop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Slop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Slop. If not, see .
*/
#include "glselectrectangle.hpp"
static Bool isDestroyNotify( Display* dpy, XEvent* ev, XPointer win ) {
return ev->type == DestroyNotify && ev->xdestroywindow.window == *((Window*)win);
}
slop::GLSelectRectangle::~GLSelectRectangle() {
if ( m_window == None ) {
return;
}
xengine->freeCRTCS( m_monitors );
delete m_framebuffer;
// Try to erase the window before destroying it.
glClearColor( 0, 0, 0, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glXSwapBuffers( xengine->m_display, m_glxWindow );
// Sleep for 0.1 seconds in hope that the rectangle was erased.
usleep( 10000 );
XDestroyWindow( xengine->m_display, m_window );
XEvent event;
// Block until the window is actually completely removed.
XIfEvent( xengine->m_display, &event, &isDestroyNotify, (XPointer)&m_window );
// Sleep for 0.1 seconds in hope that the screen actually cleared the window.
usleep( 10000 );
}
void slop::GLSelectRectangle::constrainWithinMonitor( int* x, int* y, int* w, int* h ) {
m_offsetx = 0;
m_offsety = 0;
m_offsetw = 0;
m_offseth = 0;
for ( unsigned int i=0;im_mousex >= (int)monitor->x && (int)xengine->m_mousey >= (int)monitor->y &&
(int)xengine->m_mousex <= (int)(monitor->x+monitor->width) && (int)xengine->m_mousey <= (int)(monitor->y+monitor->height) ) ) {
continue;
}
if ( (int)*x < (int)monitor->x ) {
m_offsetx = monitor->x-*x;
*w += *x-monitor->x;
*x = monitor->x;
}
if ( (int)(*x+*w) >= (int)(monitor->x+monitor->width) ) {
m_offsetw = (monitor->width-1-(*x-monitor->x+*w));
*w = monitor->width-1-(*x-monitor->x);
}
if ( (int)*y < (int)monitor->y ) {
m_offsety = monitor->y-*y;
*h += *y-monitor->y;
*y = monitor->y;
}
if ( (int)(*y+*h) >= (int)(monitor->y+monitor->height) ) {
m_offseth = (monitor->height-1-(*y-monitor->y+*h));
*h = monitor->height-1-(*y-monitor->y);
}
break;
}
m_offsetx *= m_glassSize;
m_offsety *= m_glassSize;
m_offsetw *= m_glassSize;
m_offseth *= m_glassSize;
}
void slop::GLSelectRectangle::setShader( std::string shader ) {
m_shader = shader;
m_framebuffer->setShader( shader );
}
void slop::GLSelectRectangle::setMagnifySettings( bool on, float magstrength, unsigned int pixels ) {
m_glassSize = magstrength;
m_glassPixels = pixels;
m_glassEnabled = on;
m_glassx = xengine->m_mousex;
m_glassy = xengine->m_mousey;
m_realglassx = xengine->m_mousex;
m_realglassy = xengine->m_mousey;
}
void slop::GLSelectRectangle::pushOut( int* x, int* y, int w, int h, int rx, int ry, int rw, int rh ) {
// AABB to test for collision
if (!(
*x < rx + rw &&
*x + w > rx &&
*y < ry + rh &&
h + *y > ry
)) {
// No collision, so we do nothing.
return;
}
// Otherwise we find an optimal angle to push ourselves out at.
int centerx = rx+rw/2;
int centery = ry+rh/2;
float ang = -atan2( (float)(*y+(float)h/2.f)-(float)centery, (float)(*x+(float)w/2.f)-(float)centerx );
float pi = 3.1415926535897;
float upright = pi/2 - atan( (2.f*float(rx+rw)-2.f*(float)centerx)/(float)rh );
float upleft = pi/2 - atan( (2.f*(float)rx-2.f*(float)centerx)/(float)rh );
float downright = -upright;
float downleft = -upleft;
if ( ang >= upright && ang <= upleft ) {
*x = centerx + ((rh*cos(ang))/(2*sin(ang))) - w/2;
*y = centery - rh/2 - h;
} else if ( ang <= downright && ang >= downleft) {
*x = centerx - ((rh*cos(ang))/(2*sin(ang))) - w/2;
*y = centery + rh/2;
} else if ( ang < downleft || ang > upleft ) {
*x = centerx - rw/2 - w;
*y = centery + (rw*sin(ang))/(2*cos(ang)) - h/2;
} else {
*x = centerx + rw/2;
*y = centery - (rw*sin(ang))/(2*cos(ang)) - h/2;
}
}
void slop::GLSelectRectangle::pushIn( int* x, int* y, int w, int h, int rx, int ry, int rw, int rh ) {
if ( *x > rx && *y > ry &&
*x+w < rx+rw && *y+h < ry+rh ) {
// We're already fully contained...
return;
}
// Otherwise we find an optimal angle to push ourselves in at.
int centerx = rx+rw/2;
int centery = ry+rh/2;
float ang = -atan2( (float)(*y+(float)h/2.f)-(float)centery, (float)(*x+(float)w/2.f)-(float)centerx );
float pi = 3.1415926535897;
float upright = pi/2 - atan( (2.f*float(rx+rw)-2.f*(float)centerx)/(float)rh );
float upleft = pi/2 - atan( (2.f*(float)rx-2.f*(float)centerx)/(float)rh );
float downright = -upright;
float downleft = -upleft;
if ( ang >= upright && ang <= upleft ) {
*x = centerx + ((rh*cos(ang))/(2*sin(ang))) - w/2;
*y = centery - rh/2;
} else if ( ang <= downright && ang >= downleft) {
*x = centerx - ((rh*cos(ang))/(2*sin(ang))) - w/2;
*y = centery + rh/2 - h;
} else if ( ang < downleft || ang > upleft ) {
*x = centerx - rw/2;
*y = centery + (rw*sin(ang))/(2*cos(ang)) - h/2;
} else {
*x = centerx + rw/2 - w;
*y = centery - (rw*sin(ang))/(2*cos(ang)) - h/2;
}
if ( !(*x > rx && *y > ry &&
*x+w < rx+rw && *y+h < ry+rh) ) {
if ( *x+w > rx+rw ) {
*x -= w/2;
}
if ( *x < rx ) {
*x += w/2;
}
if ( *y+h > ry+rh ) {
*y -= h/2;
}
if ( *y < ry ) {
*y += h/2;
}
}
}
void slop::GLSelectRectangle::findOptimalGlassPosition() {
// Try to move the glass next to the mouse.
m_glassx = xengine->m_mousex+m_glassPixels/2+5-m_glassBorder;
m_glassy = xengine->m_mousey+m_glassPixels/2+5-m_glassBorder;
XRectangle view, selection, combined;
view.x = xengine->m_mousex-(m_glassPixels+1+m_glassBorder)/2;
view.y = xengine->m_mousey-(m_glassPixels+1+m_glassBorder)/2;
view.width = m_glassPixels+1;
view.height = m_glassPixels+1;
selection.x = m_x-m_border;
selection.y = m_y-m_border;
selection.width = m_width+m_border*2;
selection.height = m_height+m_border*2;
combined.x = std::min( selection.x, view.x );
combined.y = std::min( selection.y, view.y );
combined.width = selection.width + std::max( selection.x-view.x, (view.x+view.width)-(selection.x+selection.width) );
combined.height = selection.height + std::max( selection.y-view.y, (view.y+view.height)-(selection.y+selection.height) );
for ( unsigned int i=0;im_mousex >= (int)monitor->x && (int)xengine->m_mousex <= (int)(monitor->x + monitor->width) &&
(int)xengine->m_mousey >= (int)monitor->y && (int)xengine->m_mousey <= (int)(monitor->y + monitor->height) ) {
pushIn( &m_glassx, &m_glassy, m_glassPixels*m_glassSize+m_glassBorder*2, m_glassPixels*m_glassSize+m_glassBorder*2, monitor->x, monitor->y, monitor->width, monitor->height );
break;
}
}
// Push the glass outside of the selection, but only if we are left clicking, and always keep it out of the "shot"
if ( xengine->getCursor() != slop::Left ) {
pushOut( &m_glassx, &m_glassy, m_glassPixels*m_glassSize+m_glassBorder*2, m_glassPixels*m_glassSize+m_glassBorder*2, combined.x, combined.y, combined.width, combined.height );
} else {
pushOut( &m_glassx, &m_glassy, m_glassPixels*m_glassSize+m_glassBorder*2, m_glassPixels*m_glassSize+m_glassBorder*2, view.x, view.y, view.width, view.height );
}
m_glassx += m_glassBorder;
m_glassy += m_glassBorder;
}
void slop::GLSelectRectangle::generateMagnifyingGlass() {
int x = xengine->m_mousex-m_glassPixels/2;
int y = xengine->m_mousey-m_glassPixels/2;
int w = m_glassPixels;
int h = m_glassPixels;
constrainWithinMonitor( &x, &y, &w, &h );
XImage* image = XGetImage( xengine->m_display, xengine->m_root, x, y, w, h, 0xffffffff, ZPixmap );
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &m_texid);
glBindTexture(GL_TEXTURE_2D, m_texid);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, (void*)(&(image->data[0])));
XDestroyImage( image );
glDisable(GL_TEXTURE_2D);
}
void slop::GLSelectRectangle::setTheme( bool on, std::string name ) {
if ( !on || name == "none" ) {
return;
}
std::string root = resource->getRealPath( name );
std::string tl = root + "/corner_tl.png";
std::string bl = root + "/corner_bl.png";
std::string tr = root + "/corner_tr.png";
std::string br = root + "/corner_br.png";
std::string straight = root + "/straight.png";
// One of the textures didn't exist, so we cancel the theme.
if (!resource->validatePath( tl ) ||
!resource->validatePath( bl ) ||
!resource->validatePath( tr ) ||
!resource->validatePath( br ) ||
!resource->validatePath( straight ) ) {
fprintf( stderr, "One of the textures was missing in the theme... disabling.\n" );
return;
}
// Otherwise we load each one :)
loadImage( &(m_cornerids[0]), tl );
loadImage( &(m_cornerids[1]), tr );
loadImage( &(m_cornerids[2]), bl );
loadImage( &(m_cornerids[3]), br );
loadImage( &(m_straightid), straight );
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &m_straightwidth );
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &m_straightheight );
m_themed = on;
}
unsigned int slop::GLSelectRectangle::loadImage( unsigned int* texture, std::string path ) {
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glGenTextures( 1, texture );
glBindTexture( GL_TEXTURE_2D, *texture );
Imlib_Load_Error err;
Imlib_Image image = imlib_load_image_with_error_return( path.c_str(), &err );
if ( err != IMLIB_LOAD_ERROR_NONE ) {
std::string message = "Failed to load image: ";
message += path;
message += "\n\t";
switch( err ) {
default: {
message += "unknown error ";
message += (int)err;
message += "\n";
break;
}
case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS:
message += "out of file descriptors\n";
break;
case IMLIB_LOAD_ERROR_OUT_OF_MEMORY:
message += "out of memory\n";
break;
case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS:
message += "path contains too many symbolic links\n";
break;
case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE:
message += "path points outside address space\n";
break;
case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY:
message += "path component is not a directory\n";
break;
case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT:
message += "path component is non-existant (~ isn't expanded inside quotes!)\n";
break;
case IMLIB_LOAD_ERROR_PATH_TOO_LONG:
message += "path is too long\n";
break;
case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT:
message += "no loader for file format (unsupported format)\n";
break;
case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: {
message += "not enough disk space\n";
break;
}
case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: {
message += "file does not exist\n";
break;
}
case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: {
message += "file is a directory\n";
break;
}
case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE:
case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: {
message += "permission denied\n";
break;
}
}
throw std::runtime_error( message.c_str() );
return *texture;
}
imlib_context_set_image( image );
DATA32* data = imlib_image_get_data_for_reading_only();
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, imlib_image_get_width(), imlib_image_get_height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, (void*)data );
if ( GLEW_VERSION_3_0 ) {
glHint( GL_GENERATE_MIPMAP_HINT, GL_NICEST );
glGenerateMipmap( GL_TEXTURE_2D );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
} else {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
imlib_free_image();
glDisable(GL_TEXTURE_2D);
return *texture;
}
slop::GLSelectRectangle::GLSelectRectangle( int sx, int sy, int ex, int ey, int border, bool highlight, float r, float g, float b, float a ) {
m_x = std::min( sx, ex );
m_y = std::min( sy, ey );
m_width = std::max( sx, ex ) - m_x;
m_height = std::max( sy, ey ) - m_y;
m_r = r;
m_g = g;
m_b = b;
m_a = a;
m_border = border;
m_window = None;
m_highlight = highlight;
m_glassPixels = 64;
m_glassx = xengine->m_mousex;
m_glassy = xengine->m_mousey;
m_realglassx = xengine->m_mousex;
m_realglassy = xengine->m_mousey;
m_glassSize = 4;
m_glassBorder = 1;
m_monitors = xengine->getCRTCS();
m_themed = false;
m_shader = "simple";
m_time = 0;
// If we don't have a border, we don't exist, so just die.
if ( m_border == 0 ) {
return;
}
if ( m_highlight ) {
m_border = 0;
}
static int visdata[] = {
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 16,
None
};
int numfbconfigs = 0;
GLXFBConfig* fbconfigs = glXChooseFBConfig( xengine->m_display, DefaultScreen( xengine->m_display ), visdata, &numfbconfigs );
m_fbconfig = 0;
for ( int i=0; im_display, fbconfigs[i] );
if ( !m_visual ) {
continue;
}
m_pictFormat = XRenderFindVisualFormat( xengine->m_display, m_visual->visual );
if ( !m_pictFormat ) {
continue;
}
m_fbconfig = fbconfigs[i];
if ( m_pictFormat->direct.alphaMask > 0 ) {
break;
}
}
if ( !m_fbconfig ) {
fprintf( stderr, "Couldn't find a matching FB config for a transparent OpenGL window!\n");
}
m_cmap = XCreateColormap( xengine->m_display, xengine->m_root, m_visual->visual, AllocNone );
XSetWindowAttributes attributes;
attributes.colormap = m_cmap;
attributes.background_pixmap = None;
attributes.border_pixmap = None;
attributes.border_pixel = 0;
// Disable window decorations.
attributes.override_redirect = True;
// Make sure we know when we've been successfully destroyed later!
attributes.event_mask = StructureNotifyMask;
unsigned long valueMask = CWOverrideRedirect | CWEventMask | CWBackPixmap | CWColormap | CWBorderPixel;
// Create the window
m_window = XCreateWindow( xengine->m_display, xengine->m_root, 0, 0, xengine->getWidth(), xengine->getHeight(),
0, m_visual->depth, InputOutput,
m_visual->visual, valueMask, &attributes );
if ( !m_window ) {
fprintf( stderr, "Couldn't create a GL window!\n");
}
m_glxWindow = m_window;
static char title[] = "OpenGL Slop";
XWMHints* startup_state = XAllocWMHints();
startup_state->initial_state = NormalState;
startup_state->flags = StateHint;
XTextProperty textprop;
textprop.value = (unsigned char*)title;
textprop.encoding = XA_STRING;
textprop.format = 8;
textprop.nitems = strlen( title );
XSizeHints sizehints;
sizehints.x = 0;
sizehints.y = 0;
sizehints.width = xengine->getWidth();
sizehints.height = xengine->getHeight();
sizehints.flags = USPosition | USSize;
XClassHint classhints;
char name[] = "slop";
classhints.res_name = name;
classhints.res_class = name;
XSetClassHint( xengine->m_display, m_window, &classhints );
XSetWMProperties( xengine->m_display, m_window, &textprop, &textprop, NULL, 0, &sizehints, startup_state, NULL );
XFree( startup_state );
// Make it so all input falls through
XRectangle rect;
rect.x = rect.y = rect.width = rect.height = 0;
XShapeCombineRectangles( xengine->m_display, m_window, ShapeInput, 0, 0, &rect, 1, ShapeSet, 0);
XMapWindow( xengine->m_display, m_window );
int dummy;
if ( !glXQueryExtension( xengine->m_display, &dummy, &dummy ) ) {
fprintf( stderr, "OpenGL is not supported!\n" );
}
m_renderContext = glXCreateNewContext( xengine->m_display, m_fbconfig, GLX_RGBA_TYPE, 0, True );
if ( !m_renderContext ) {
fprintf( stderr, "Failed to create a GL context.\n" );
}
if ( !glXMakeContextCurrent( xengine->m_display, m_glxWindow, m_glxWindow, m_renderContext ) ) {
fprintf( stderr, "Failed to attach GL context to window!\n" );
}
GLenum err = glewInit();
if ( GLEW_OK != err ) {
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
}
// Get an image of the entire desktop for use in shaders.
XImage* image = XGetImage( xengine->m_display, xengine->m_root, 0, 0, xengine->getWidth(), xengine->getHeight(), 0xffffffff, ZPixmap );
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &m_desktop);
glBindTexture(GL_TEXTURE_2D, m_desktop);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, xengine->getWidth(), xengine->getHeight(), 0, GL_BGRA, GL_UNSIGNED_BYTE, (void*)(&(image->data[0])));
XDestroyImage( image );
glDisable(GL_TEXTURE_2D);
m_framebuffer = new slop::Framebuffer( xengine->getWidth(), xengine->getHeight(), slop::Framebuffer::color, m_shader );
m_framebuffer->bind();
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glClearColor( 0, 0, 0, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glXSwapBuffers( xengine->m_display, m_glxWindow );
m_framebuffer->unbind();
}
void slop::GLSelectRectangle::setGeo( int sx, int sy, int ex, int ey ) {
int x = std::min( sx, ex );
int y = std::min( sy, ey );
int w = std::max( sx, ex ) - x;
int h = std::max( sy, ey ) - y;
m_x = x;
m_y = y;
m_width = w;
m_height = h;
}
void slop::GLSelectRectangle::update( double dt ) {
m_time += dt;
m_framebuffer->bind();
glViewport( 0, 0, xengine->getWidth(), xengine->getHeight() );
glClearColor( 0, 0, 0, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( 0, xengine->getWidth(), xengine->getHeight(), 0, 1, -1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
if ( !m_themed ) {
glColor4f( m_r, m_g, m_b, m_a );
glRecti( m_x-m_border, m_y, m_x+m_width+m_border, m_y-m_border );
glRecti( m_x-m_border, m_y+m_height, m_x+m_width+m_border, m_y+m_height+m_border );
glRecti( m_x-m_border, m_y, m_x, m_y+m_height );
glRecti( m_x+m_width, m_y, m_x+m_width+m_border, m_y+m_height );
} else {
glColor4f( m_r, m_g, m_b, m_a );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, m_straightid );
float something = (float)(m_border)/(float)m_straightheight;
float txoffset = (((float)m_width+m_border)/(float)m_straightwidth)/something;
float tyoffset = (((float)m_height+m_border)/(float)m_straightwidth)/something;
//float ratio = ((float)m_straightwidth/(float)m_straightheight);
glBegin( GL_QUADS );
// straight top
glTexCoord2f(0.0, 0.0); glVertex2f( m_x-m_border/2, m_y-m_border );
glTexCoord2f(txoffset, 0.0); glVertex2f( m_x+m_width+m_border/2, m_y-m_border );
glTexCoord2f(txoffset, 1.0); glVertex2f( m_x+m_width+m_border/2, m_y );
glTexCoord2f(0.0, 1.0); glVertex2f( m_x-m_border/2, m_y );
// straight bot
glTexCoord2f(0.0, 0.0); glVertex2f( m_x-m_border/2, m_y+m_height );
glTexCoord2f(txoffset, 0.0); glVertex2f( m_x+m_width+m_border/2, m_y+m_height );
glTexCoord2f(txoffset, 1.0); glVertex2f( m_x+m_width+m_border/2, m_y+m_height+m_border );
glTexCoord2f(0.0, 1.0); glVertex2f( m_x-m_border/2, m_y+m_height+m_border );
// straight left
glTexCoord2f(0.0, 1.0); glVertex2f( m_x-m_border, m_y-m_border/2 );
glTexCoord2f(0.0, 0.0); glVertex2f( m_x, m_y-m_border/2 );
glTexCoord2f(tyoffset, 0.0); glVertex2f( m_x, m_y+m_height+m_border/2 );
glTexCoord2f(tyoffset, 1.0); glVertex2f( m_x-m_border, m_y+m_height+m_border/2 );
// straight right
glTexCoord2f(0.0, 1.0); glVertex2f( m_x+m_width, m_y-m_border/2 );
glTexCoord2f(0.0, 0.0); glVertex2f( m_x+m_width+m_border, m_y-m_border/2 );
glTexCoord2f(tyoffset, 0.0); glVertex2f( m_x+m_width+m_border, m_y+m_height+m_border/2 );
glTexCoord2f(tyoffset, 1.0); glVertex2f( m_x+m_width, m_y+m_height+m_border/2 );
glEnd();
// top left corner
glBindTexture( GL_TEXTURE_2D, m_cornerids[0] );
glBegin( GL_QUADS );
glTexCoord2f(0.0, 0.0); glVertex2f( m_x-m_border, m_y-m_border );
glTexCoord2f(1.0, 0.0); glVertex2f( m_x, m_y-m_border );
glTexCoord2f(1.0, 1.0); glVertex2f( m_x, m_y );
glTexCoord2f(0.0, 1.0); glVertex2f( m_x-m_border, m_y );
glEnd();
// top right
glBindTexture( GL_TEXTURE_2D, m_cornerids[1] );
glBegin( GL_QUADS );
glTexCoord2f(0.0, 0.0); glVertex2f( m_x+m_width, m_y-m_border );
glTexCoord2f(1.0, 0.0); glVertex2f( m_x+m_width+m_border, m_y-m_border );
glTexCoord2f(1.0, 1.0); glVertex2f( m_x+m_width+m_border, m_y );
glTexCoord2f(0.0, 1.0); glVertex2f( m_x+m_width, m_y );
glEnd();
// bottom left
glBindTexture( GL_TEXTURE_2D, m_cornerids[2] );
glBegin( GL_QUADS );
glTexCoord2f(0.0, 0.0); glVertex2f( m_x-m_border, m_y+m_height );
glTexCoord2f(1.0, 0.0); glVertex2f( m_x, m_y+m_height );
glTexCoord2f(1.0, 1.0); glVertex2f( m_x, m_y+m_height+m_border );
glTexCoord2f(0.0, 1.0); glVertex2f( m_x-m_border, m_y+m_height+m_border );
glEnd();
// bottom right
glBindTexture( GL_TEXTURE_2D, m_cornerids[2] );
glBegin( GL_QUADS );
glTexCoord2f(0.0, 0.0); glVertex2f( m_x+m_width, m_y+m_height );
glTexCoord2f(1.0, 0.0); glVertex2f( m_x+m_width+m_border, m_y+m_height );
glTexCoord2f(1.0, 1.0); glVertex2f( m_x+m_width+m_border, m_y+m_height+m_border );
glTexCoord2f(0.0, 1.0); glVertex2f( m_x+m_width, m_y+m_height+m_border );
glEnd();
}
m_framebuffer->unbind();
glClearColor( 0, 0, 0, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
m_framebuffer->draw( (float) m_time, m_desktop );
if ( m_glassEnabled ) {
generateMagnifyingGlass();
findOptimalGlassPosition();
// Takes .1 second to reach the real position. Used for easing.
m_realglassx -= float(m_realglassx - (float)m_glassx) * dt * 10;
m_realglassy -= float(m_realglassy - (float)m_glassy) * dt * 10;
// Black outline...
glColor4f( 0, 0, 0, 1 );
glBegin( GL_QUADS );
glTexCoord2f(0.0, 1.0); glVertex3f( m_realglassx+m_offsetx-m_glassBorder, m_realglassy+(m_glassSize*m_glassPixels)+m_offseth+m_glassBorder, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f( m_realglassx+(m_glassSize*m_glassPixels)+m_offsetw+m_glassBorder, m_realglassy+(m_glassSize*m_glassPixels)+m_offseth+1, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f( m_realglassx+(m_glassSize*m_glassPixels)+m_offsetw+m_glassBorder, m_realglassy+m_offsety-m_glassBorder, 0.0);
glTexCoord2f(0.0, 0.0); glVertex3f( m_realglassx+m_offsetx-m_glassBorder, m_realglassy+m_offsety-m_glassBorder, 0.0);
glEnd();
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, m_texid );
glColor4f( 1, 1, 1, 1 );
glBegin( GL_QUADS );
glTexCoord2f(0.0, 1.0); glVertex3f( m_realglassx+m_offsetx, m_realglassy+(m_glassSize*m_glassPixels)+m_offseth, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f( m_realglassx+(m_glassSize*m_glassPixels)+m_offsetw, m_realglassy+(m_glassSize*m_glassPixels)+m_offseth, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f( m_realglassx+(m_glassSize*m_glassPixels)+m_offsetw, m_realglassy+m_offsety, 0.0);
glTexCoord2f(0.0, 0.0); glVertex3f( m_realglassx+m_offsetx, m_realglassy+m_offsety, 0.0);
glEnd();
glDisable( GL_TEXTURE_2D );
glLogicOp(GL_INVERT);
glEnable(GL_COLOR_LOGIC_OP);
glLineWidth( 2 );
glColor4f( 0, 0, 0, 1 );
glBegin( GL_LINES );
float cx = m_realglassx+(m_glassSize*m_glassPixels)/2;
float cy = m_realglassy+(m_glassSize*m_glassPixels)/2;
glVertex3f( cx-5, cy, 0 );
glVertex3f( cx+5, cy, 0 );
glVertex3f( cx, cy-5, 0 );
glVertex3f( cx, cy+5, 0 );
glEnd();
glLogicOp(GL_NOOP);
glDisable(GL_COLOR_LOGIC_OP);
}
glXSwapBuffers( xengine->m_display, m_glxWindow );
}