iup-stack/iup/srcglcontrols/iup_glsubcanvas.c

548 lines
15 KiB
C
Raw Permalink Normal View History

2023-02-20 16:44:45 +00:00
/** \file
* \brief GLSubCanvas Control.
*
* See Copyright Notice in "iup.h"
*/
#ifdef WIN32
#include <windows.h>
#endif
#if defined (__APPLE__) || defined (OSX)
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "iup.h"
#include "iupcbs.h"
#include "iupgl.h"
#include "iup_assert.h"
#include "iup_object.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_layout.h"
#include "iup_glcontrols.h"
#include "iup_glfont.h"
#include "iup_glsubcanvas.h"
static void iGLFixRectY(Ihandle* gl_parent, int *y, int h)
{
int border = iupAttribGetBoolean(gl_parent, "BORDER");
/* y is at bottom and oriented bottom to top in OpenGL, both different from IUP */
*y = *y + h - 1; /* First, move to bottom */
*y = gl_parent->currentheight - 2 * border - 1 - *y; /* Second, orient bottom to top */
}
static void iGLSaveClientClipping(Ihandle* ih, int clip_x, int clip_y, int clip_w, int clip_h)
{
int x = 0, y = 0, w = 0, h = 0;
IupGetIntInt(ih, "CLIENTSIZE", &w, &h);
IupGetIntInt(ih, "CLIP_MIN", &x, &y);
x += ih->x;
y += ih->y;
if (w <= 0 || h <= 0)
return;
if (x > clip_x + clip_w)
return;
if (y > clip_y + clip_h)
return;
if (x < clip_x)
x = clip_x;
if (y < clip_y)
y = clip_y;
if (x + w < clip_x)
return;
if (y + h < clip_y)
return;
if (x + w > clip_x + clip_w)
w = clip_x + clip_w - x;
if (y + h > clip_y + clip_h)
h = clip_y + clip_h - y;
if (w < 0) w = 0;
if (h < 0) h = 0;
iupAttribSetInt(ih, "CLIP_X", x);
iupAttribSetInt(ih, "CLIP_Y", y);
iupAttribSetInt(ih, "CLIP_W", w);
iupAttribSetInt(ih, "CLIP_H", h);
}
static int iGLSetClipping(Ihandle* ih, int x, int y, int w, int h, Ihandle* gl_parent)
{
int clip_x = iupAttribGetInt(ih->parent, "CLIP_X");
int clip_y = iupAttribGetInt(ih->parent, "CLIP_Y");
int clip_w = iupAttribGetInt(ih->parent, "CLIP_W");
int clip_h = iupAttribGetInt(ih->parent, "CLIP_H");
iupAttribSet(ih, "CLIP_W", "0");
iupAttribSet(ih, "CLIP_H", "0");
if (clip_w <= 0 || clip_h <= 0)
return 0;
if (w <= 0 || h <= 0)
return 0;
if (x > clip_x + clip_w)
return 0;
if (y > clip_y + clip_h)
return 0;
if (x < clip_x)
x = clip_x;
if (y < clip_y)
y = clip_y;
if (x + w < clip_x)
return 0;
if (y + h < clip_y)
return 0;
if (x + w > clip_x + clip_w)
w = clip_x + clip_w - x;
if (y + h > clip_y + clip_h)
h = clip_y + clip_h - y;
if (w < 0) w = 0;
if (h < 0) h = 0;
if (w == 0 || h == 0)
return 0;
if (ih->firstchild)
iGLSaveClientClipping(ih, clip_x, clip_y, clip_w, clip_h);
iGLFixRectY(gl_parent, &y, h);
glScissor(x, y, w, h);
return 1;
}
int iupGLSubCanvasSetTransform(Ihandle* ih, Ihandle* gl_parent)
{
int x = ih->x;
int y = ih->y;
int w = ih->currentwidth;
int h = ih->currentheight;
/* crop to parent's rectangle */
if (!iGLSetClipping(ih, x, y, w, h, gl_parent))
return 0;
iGLFixRectY(gl_parent, &y, h);
glViewport(x, y, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.375, 0.375, 0.0); /* render all primitives at integer positions */
/* An optimum compromise that allows all primitives to be specified at integer positions,
while still ensuring predictable rasterization, is to translate x and y by 0.375.
Such a translation keeps polygon and pixel image edges safely away from the centers of pixels,
while moving line vertices close enough to the pixel centers.
From: OpenGL Programming Guide (Red Book) - Appendix G "Programming Tips" - OpenGL Correctness Tips
*/
return 1;
}
void iupGLSubCanvasSaveState(Ihandle* gl_parent)
{
int border = iupAttribGetBoolean(gl_parent, "BORDER");
char* saved = iupAttribGet(gl_parent, "_IUP_GLSUBCANVAS_SAVED");
iupASSERT(saved == NULL);
if (saved)
return;
/* save all changes, except transformation matrix */
glPushAttrib(GL_ENABLE_BIT | GL_HINT_BIT | GL_VIEWPORT_BIT);
/* save transformation matrix */
glMatrixMode(GL_PROJECTION);
glPushMatrix();
/* save transformation matrix */
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
/* clipping */
glEnable(GL_SCISSOR_TEST);
/* alpha enabled */
glEnable(GL_BLEND); /* GL_COLOR_BUFFER_BIT or GL_ENABLE_BIT */
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
/* image data alignment */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* not saved */
/* disable Z axis */
glDisable(GL_DEPTH_TEST); /* GL_DEPTH_BUFFER_BIT or GL_ENABLE_BIT */
/* disable lighting */
glDisable(GL_LIGHTING);
/* anti-alias */
glEnable(GL_POINT_SMOOTH); /* GL_LINE_BIT or GL_ENABLE_BIT */
glEnable(GL_LINE_SMOOTH); /* GL_POINT_BIT or GL_ENABLE_BIT */
glEnable(GL_POLYGON_SMOOTH); /* GL_POLYGON_BIT or GL_ENABLE_BIT */
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); /* GL_HINT_BIT */
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
iupAttribSet(gl_parent, "_IUP_GLSUBCANVAS_SAVED", "1");
iupAttribSetInt(gl_parent, "CLIP_X", 0);
iupAttribSetInt(gl_parent, "CLIP_Y", 0);
iupAttribSetInt(gl_parent, "CLIP_W", gl_parent->currentwidth - 2 * border);
iupAttribSetInt(gl_parent, "CLIP_H", gl_parent->currentheight - 2 * border);
}
void iupGLSubCanvasRestoreState(Ihandle* gl_parent)
{
char* saved = iupAttribGet(gl_parent, "_IUP_GLSUBCANVAS_SAVED");
iupASSERT(saved != NULL);
if (!saved)
return;
/* restore transformation matrix */
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
/* restore transformation matrix */
glMatrixMode(GL_PROJECTION);
glPopMatrix();
/* restore everything else */
glPopAttrib();
iupAttribSet(gl_parent, "_IUP_GLSUBCANVAS_SAVED", NULL);
}
static void iGLSubCanvasRedrawFront(Ihandle* ih)
{
IFn cb = (IFn)IupGetCallback(ih, "GL_ACTION");
if (cb && iupAttribGetInt(ih, "VISIBLE"))
{
Ihandle* gl_parent = (Ihandle*)iupAttribGet(ih, "_IUP_GLCANVAS_PARENT");
IupGLMakeCurrent(gl_parent);
glDrawBuffer(GL_FRONT);
iupGLSubCanvasSaveState(gl_parent);
if (iupGLSubCanvasSetTransform(ih, gl_parent))
cb(ih);
iupGLSubCanvasRestoreState(gl_parent);
glDrawBuffer(GL_BACK);
}
}
int iupGLSubCanvasRedraw(Ihandle* ih)
{
Ihandle* gl_parent = (Ihandle*)iupAttribGet(ih, "_IUP_GLCANVAS_PARENT");
if (iupAttribGetInt(ih, "REDRAWALL"))
IupSetAttribute(gl_parent, "REDRAW", NULL); /* redraw the whole box */
else
iGLSubCanvasRedrawFront(ih); /* redraw only the sub-canvas in the front buffer */
return IUP_DEFAULT; /* return IUP_DEFAULT so it can be used as a callback */
}
void iupGLSubCanvasStartMoving(Ihandle* ih, int x, int y)
{
/* Used for MOVABLE controls */
iupAttribSetInt(ih, "_IUP_GLSUBCANVAS_START_X", ih->x + x);
iupAttribSetInt(ih, "_IUP_GLSUBCANVAS_START_Y", ih->y + y);
if (iupAttribGetInt(ih, "MOVETOTOP"))
IupSetAttribute(ih, "ZORDER", "TOP");
}
int iupGLSubCanvasMove(Ihandle* ih, int x, int y)
{
/* Used for MOVABLE controls */
int moved = 0;
int start_x = iupAttribGetInt(ih, "_IUP_GLSUBCANVAS_START_X");
int start_y = iupAttribGetInt(ih, "_IUP_GLSUBCANVAS_START_Y");
x += ih->x;
y += ih->y;
if ((x != start_x) || (y != start_y))
{
Ihandle* gl_parent = (Ihandle*)iupAttribGet(ih, "_IUP_GLCANVAS_PARENT");
IFnii cb = (IFnii)IupGetCallback(ih, "MOVE_CB");
/* clear canvas box alignment */
iupAttribSet(ih, "VERTICALALIGN", NULL);
iupAttribSet(ih, "HORIZONTALALIGN", NULL);
iupBaseSetPosition(ih, ih->x + (x - start_x), ih->y + (y - start_y));
iupLayoutUpdate(ih);
IupSetAttribute(gl_parent, "REDRAW", NULL); /* redraw the whole box */
moved = 1;
if (cb)
cb(ih, ih->x, ih->y);
}
iupAttribSetInt(ih, "_IUP_GLSUBCANVAS_START_X", x);
iupAttribSetInt(ih, "_IUP_GLSUBCANVAS_START_Y", y);
return moved;
}
/****************************************************************/
static char* iGLSubCanvasGetCharSizeAttrib(Ihandle* ih)
{
int charwidth, charheight;
iupGLFontGetCharSize(ih, &charwidth, &charheight);
if (charwidth == 0 || charheight == 0)
return NULL;
return iupStrReturnIntInt(charwidth, charheight, 'x');
}
static int iGLSubCanvasSetSizeAttrib(Ihandle* ih, const char* value)
{
if (!value)
{
ih->userwidth = 0;
ih->userheight = 0;
}
else
{
/* if not specified, the value is 0 */
int w = 0, h = 0;
int charwidth, charheight;
iupGLFontGetCharSize(ih, &charwidth, &charheight);
iupStrToIntInt(value, &w, &h, 'x');
if (w < 0) w = 0;
if (h < 0) h = 0;
ih->userwidth = iupWIDTH2RASTER(w, charwidth);
ih->userheight = iupHEIGHT2RASTER(h, charheight);
}
return 1; /* always save in the hash table, so when FONT is changed SIZE can be updated */
}
static char* iGLSubCanvasGetSizeAttrib(Ihandle* ih)
{
int charwidth, charheight, width, height;
if (ih->handle)
{
width = ih->currentwidth;
height = ih->currentheight;
}
else
{
width = ih->userwidth;
height = ih->userheight;
}
iupGLFontGetCharSize(ih, &charwidth, &charheight);
if (charwidth == 0 || charheight == 0)
return NULL; /* if font failed get from the hash table */
return iupStrReturnIntInt(iupRASTER2WIDTH(width, charwidth), iupRASTER2HEIGHT(height, charheight), 'x');
}
static int iGLSubCanvasSetRedrawFrontAttrib(Ihandle* ih, const char* value)
{
iGLSubCanvasRedrawFront(ih);
(void)value;
return 0;
}
static int iGLSubCanvasSetZorder(Ihandle* parent, Ihandle* child, int top)
{
Ihandle *c,
*c_prev = NULL;
if (top && child->brother == NULL) /* already at the top */
return 0;
if (!top && parent->firstchild == child) /* already at the bottom */
return 0;
/* Removes the child entry inside the parent's child list */
for (c = parent->firstchild; c; c = c->brother)
{
if (c == child)
{
if (c_prev == NULL)
parent->firstchild = child->brother;
else
c_prev->brother = child->brother;
child->brother = NULL;
break;
}
c_prev = c;
}
if (top)
{
/* insert at last element */
for (c = parent->firstchild; c; c = c->brother)
{
if (!c->brother)
{
c->brother = child;
break;
}
}
}
else
{
/* insert at first */
child->brother = parent->firstchild;
parent->firstchild = child;
}
return 1;
}
static int iGLSubCanvasSetZorderAttrib(Ihandle* ih, const char* value)
{
Ihandle* gl_parent = (Ihandle*)iupAttribGet(ih, "_IUP_GLCANVAS_PARENT");
int redraw = 0;
int top = 1;
if (iupStrEqualNoCase(value, "BOTTOM"))
top = 0;
/* move everyone in the same hierarchy */
while (ih != gl_parent)
{
if (iGLSubCanvasSetZorder(ih->parent, ih, top))
redraw = 1;
ih = ih->parent;
}
if (redraw)
IupSetAttribute(gl_parent, "REDRAW", NULL); /* redraw the whole box */
return 0;
}
/****************************************************************/
static int iGLSubCanvasMapMethod(Ihandle* ih)
{
Ihandle* gl_parent;
for (gl_parent = ih->parent; gl_parent; gl_parent = gl_parent->parent)
{
if (gl_parent->iclass->nativetype == IUP_TYPECANVAS)
break;
}
/* must have a canvas parent, and it must be a glcanvasbox */
if (!gl_parent || !iupClassMatch(gl_parent->iclass, "glcanvasbox"))
return IUP_ERROR;
iupAttribSet(ih, "_IUP_GLCANVAS_PARENT", (char*)gl_parent);
/* use the handle of the native parent */
ih->handle = gl_parent->handle;
return IUP_NOERROR;
}
static void iGLSubCanvasReleaseMethod(Iclass* ic)
{
iupGLFontFinish();
(void)ic;
}
static int iGLSubCanvasCreateMethod(Ihandle* ih, void** params)
{
(void)params;
// to avoid system fonts that are not well processed by FreeType
iupAttribSetClassObject(ih, "FONTFACE", "Helvetica");
return IUP_NOERROR;
}
Iclass* iupGLSubCanvasNewClass(void)
{
Iclass* ic = iupClassNew(NULL);
ic->name = "glsubcanvas";
ic->cons = "GLSubCanvas";
ic->format = NULL; /* no parameters */
ic->nativetype = IUP_TYPEVOID;
ic->childtype = IUP_CHILDNONE;
ic->is_interactive = 0;
/* Class functions */
ic->New = iupGLSubCanvasNewClass;
ic->Create = iGLSubCanvasCreateMethod;
ic->Release = iGLSubCanvasReleaseMethod;
ic->Map = iGLSubCanvasMapMethod;
/* Callbacks */
iupClassRegisterCallback(ic, "GL_ACTION", "");
iupClassRegisterCallback(ic, "GL_BUTTON_CB", "iiiis");
iupClassRegisterCallback(ic, "GL_ENTERWINDOW_CB", "ii");
iupClassRegisterCallback(ic, "GL_LEAVEWINDOW_CB", "");
iupClassRegisterCallback(ic, "GL_MOTION_CB", "iis");
iupClassRegisterCallback(ic, "GL_WHEEL_CB", "fiis");
/* Base Callbacks */
iupBaseRegisterBaseCallbacks(ic);
/* Common */
iupBaseRegisterCommonAttrib(ic);
/* redefine common attributes */
iupClassRegisterAttribute(ic, "SIZE", iGLSubCanvasGetSizeAttrib, iGLSubCanvasSetSizeAttrib, NULL, NULL, IUPAF_NO_SAVE | IUPAF_NO_DEFAULTVALUE | IUPAF_NOT_MAPPED | IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "CHARSIZE", iGLSubCanvasGetCharSizeAttrib, NULL, NULL, NULL, IUPAF_NO_DEFAULTVALUE | IUPAF_READONLY | IUPAF_NOT_MAPPED | IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "FONT", NULL, iupGLFontSetFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED); /* inherited */
/* Visual */
iupBaseRegisterVisualAttrib(ic);
/* redefine native visual attributes */
iupClassRegisterAttribute(ic, "VISIBLE", NULL, NULL, IUPAF_SAMEASSYSTEM, "YES", IUPAF_NO_SAVE | IUPAF_DEFAULT); /* inheritable */
iupClassRegisterAttribute(ic, "ACTIVE", NULL, NULL, IUPAF_SAMEASSYSTEM, "YES", IUPAF_DEFAULT); /* inheritable */
iupClassRegisterAttribute(ic, "ZORDER", NULL, iGLSubCanvasSetZorderAttrib, NULL, NULL, IUPAF_WRITEONLY | IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "TIP", NULL, NULL, NULL, NULL, IUPAF_NO_DEFAULTVALUE | IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "TIPVISIBLE", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "CURSOR", NULL, NULL, IUPAF_SAMEASSYSTEM, "ARROW", IUPAF_IHANDLENAME | IUPAF_NO_INHERIT);
/* Common visual */
iupClassRegisterAttribute(ic, "BORDERCOLOR", NULL, NULL, IUPAF_SAMEASSYSTEM, "50 150 255", IUPAF_DEFAULT); /* inheritable */
iupClassRegisterAttribute(ic, "BORDERWIDTH", NULL, NULL, IUPAF_SAMEASSYSTEM, "1", IUPAF_DEFAULT); /* inheritable */
iupClassRegisterAttribute(ic, "BGCOLOR", NULL, NULL, NULL, NULL, IUPAF_DEFAULT); /* inheritable */
iupClassRegisterAttribute(ic, "HLCOLOR", NULL, NULL, IUPAF_SAMEASSYSTEM, "200 225 245", IUPAF_DEFAULT); /* inheritable */
iupClassRegisterAttribute(ic, "PSCOLOR", NULL, NULL, IUPAF_SAMEASSYSTEM, "150 200 235", IUPAF_DEFAULT); /* inheritable */
iupClassRegisterAttribute(ic, "UNDERLINE", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "REDRAWFRONT", NULL, iGLSubCanvasSetRedrawFrontAttrib, NULL, NULL, IUPAF_WRITEONLY | IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "REDRAWALL", NULL, NULL, IUPAF_SAMEASSYSTEM, "Yes", IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "HIGHLIGHT", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "PRESSED", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT);
iupGLFontInit();
return ic;
}
Ihandle* IupGLSubCanvas(void)
{
return IupCreate("glsubcanvas");
}