e16/src/glwin.c

769 lines
17 KiB
C

/*
* Copyright (C) 2007-2022 Kim Woelders
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of the Software, its documentation and marketing & publicity
* materials, and acknowledgment shall be given in the documentation, materials
* and software packages that this Software was used.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include <X11/keysym.h>
#include "E.h"
#include "animation.h"
#include "cursors.h"
#include "desktops.h"
#include "eimage.h"
#include "eglx.h"
#include "emodule.h"
#include "eobj.h"
#include "events.h"
#include "ewins.h"
#include "grabs.h"
#include "util.h"
#define ENABLE_DEBUG 1
#if ENABLE_DEBUG
#define Dprintf(fmt...) do { if(EDebug(EDBUG_TYPE_GLX))Eprintf(fmt); } while(0)
#define D2printf(fmt...) do { if(EDebug(EDBUG_TYPE_GLX)>1)Eprintf(fmt); } while(0)
#else
#define Dprintf(fmt...)
#define D2printf(fmt...)
#endif /* ENABLE_DEBUG */
static struct {
int mode;
} Conf_glwin;
static struct {
char active;
} Mode_glwin;
typedef struct {
EObj *eo;
char grabbing;
EWin *ewin;
} GLWindow;
static void GlwinExit(void);
static GLWindow GLWin;
static const char *image = "pix/about.png";
static char light; /* Lighting on/off */
static GLfloat rot_x; /* X rotation */
static GLfloat rot_y; /* Y rotation */
static GLfloat speed_x; /* X rotation speed */
static GLfloat speed_y; /* Y rotation speed */
static GLfloat bg_z; /* Background z */
static unsigned int t0, tn;
#define N_TEXTURES 5
static unsigned int sel_bg;
static unsigned int filter;
static ETexture *texture[N_TEXTURES];
static int sel_ewin;
static unsigned int
GetDTime(void)
{
return GetTimeMs() - t0;
}
static void
TexturesLoad(void)
{
EImage *im;
/* Texture 0 - None */
texture[0] = NULL;
if (!texture[1])
{
im = ThemeImageLoad(image);
if (!im)
{
Eprintf("Could not load: %s\n", image);
}
else
{
/* Texture 1 - Filter: None */
texture[1] = EGlTextureFromImage(im, 0);
/* Texture 2 - Filter: Linear */
texture[2] = EGlTextureFromImage(im, 1);
/* Texture 3 - Mipmap */
texture[3] = EGlTextureFromImage(im, 2);
EImageFree(im);
}
}
if (!texture[4])
{
/* Texture 4 - BG pixmap */
texture[4] =
EGlTextureFromDrawable(DeskGetBackgroundPixmap(DesksGetCurrent()),
0);
}
}
static void
SceneResize(unsigned int width, unsigned int height)
{
Dprintf("SceneResize\n");
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, 0, height, -1000.f, 1000.f);
glMatrixMode(GL_MODELVIEW);
}
static GLfloat light_ambient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
static GLfloat light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
static GLfloat light_position[] = { 0.0f, 0.0f, 2.0f, 1.0f };
static void
SceneInitLight(void)
{
glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT1, GL_POSITION, light_position);
glEnable(GL_LIGHT1);
}
#define L 0.0f
#define R 1.0f
#define T 0.0f
#define B 1.0f
static void
DrawBackground(ETexture *et, GLfloat w, GLfloat h)
{
if (!et)
return;
glBindTexture(et->target, et->texture);
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(L, B);
glVertex3f(0, 0, bg_z);
glTexCoord2f(L, T);
glVertex3f(0, h, bg_z);
glTexCoord2f(R, T);
glVertex3f(w, h, bg_z);
glTexCoord2f(R, B);
glVertex3f(w, 0, bg_z);
glEnd();
}
static void
DrawQube(ETexture *et, GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h,
GLfloat rx, GLfloat ry)
{
GLfloat w2, h2, t;
if (!et)
return;
glBindTexture(et->target, et->texture);
switch (filter)
{
default:
case 0:
glTexParameteri(et->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(et->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
break;
case 1:
glTexParameteri(et->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(et->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
break;
}
glPushMatrix();
#if 0
x = round(x);
y = round(y);
#endif
glTranslatef(x, y, z);
#if 0
glScalef(sz, sz, sz);
#endif
glRotatef(rx, 1.0f, 0.0f, 0.0f); /* Rotate around X axis */
glRotatef(ry, 0.0f, 1.0f, 0.0f); /* Rotate around Y axis */
t = 4.0f;
w2 = round(w / 2.f);
h2 = round(h / 2.f);
glBegin(GL_QUADS);
#if 1
/* Front */
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(L, B);
glVertex3f(-w2, -h2, t);
glTexCoord2f(R, B);
glVertex3f(w2, -h2, t);
glTexCoord2f(R, T);
glVertex3f(w2, h2, t);
glTexCoord2f(L, T);
glVertex3f(-w2, h2, t);
#endif
#if 1
/* Back */
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(L, B);
glVertex3f(w2, -h2, -t);
glTexCoord2f(R, B);
glVertex3f(-w2, -h2, -t);
glTexCoord2f(R, T);
glVertex3f(-w2, h2, -t);
glTexCoord2f(L, T);
glVertex3f(w2, h2, -t);
#endif
#if 1
/* Right */
glNormal3f(1.0f, 0.0f, 0.0f);
glTexCoord2f(L, B);
glVertex3f(w2, -h2, t);
glTexCoord2f(R, B);
glVertex3f(w2, -h2, -t);
glTexCoord2f(R, T);
glVertex3f(w2, h2, -t);
glTexCoord2f(L, T);
glVertex3f(w2, h2, t);
#endif
#if 1
/* Left */
glNormal3f(-1.0f, 0.0f, 0.0f);
glTexCoord2f(L, B);
glVertex3f(-w2, -h2, -t);
glTexCoord2f(R, B);
glVertex3f(-w2, -h2, t);
glTexCoord2f(R, T);
glVertex3f(-w2, h2, t);
glTexCoord2f(L, T);
glVertex3f(-w2, h2, -t);
#endif
#if 1
/* Top */
glNormal3f(0.0f, 1.0f, 0.0f);
glTexCoord2f(L, B);
glVertex3f(w2, h2, t);
glTexCoord2f(R, B);
glVertex3f(w2, h2, -t);
glTexCoord2f(R, T);
glVertex3f(-w2, h2, -t);
glTexCoord2f(L, T);
glVertex3f(-w2, h2, t);
#endif
#if 1
/* Bottom */
glNormal3f(0.0f, -1.0f, 0.0f);
glTexCoord2f(L, B);
glVertex3f(w2, -h2, -t);
glTexCoord2f(R, B);
glVertex3f(w2, -h2, t);
glTexCoord2f(R, T);
glVertex3f(-w2, -h2, t);
glTexCoord2f(L, T);
glVertex3f(-w2, -h2, -t);
#endif
glEnd();
glPopMatrix();
}
static void
SceneDraw2(unsigned int tms, EWin **ewins, int num)
{
double t;
int i, j, k, nx, ny;
GLfloat x, y, w, h, dx, dy, sz;
EObj *eo;
t = 1e-3 * tms;
w = EobjGetW(GLWin.eo);
h = EobjGetH(GLWin.eo);
w = (3 * w) / 4;
DrawBackground(texture[sel_bg], w, h);
i = (int)sqrt(w * h / (1.0 * num));
nx = (int)((w + i - 1) / i);
if (nx <= 0)
nx = 1;
ny = (num + nx - 1) / nx;
if (ny <= 0)
ny = 1;
#if 0
Eprintf("wxh=%fx%f num=%d nx,ny=%d,%d\n", w, h, num, nx, ny);
#endif
w = EobjGetW(GLWin.eo) / nx;
h = EobjGetH(GLWin.eo) / ny;
k = 0;
for (j = 0; j < ny; j++)
{
for (i = 0; i < nx; i++)
{
if (k >= num)
break;
x = i * w;
y = j * h;
eo = EoObj(ewins[k]);
dx = 100.0f * exp(-t);
dx = (fabs(dx) < 1.0) ? 0. : dx * sin(5. * t);
dy = 100.0f * exp(-t);
dy = (fabs(dy) < 1.0) ? 0. : dy * cos(5. * t);
sz = (k == sel_ewin) ? 0.6f : 0.5f;
DrawQube(EobjGetTexture(eo),
dx + (0.5f + i) * w, dy + (0.5f + j) * h, 500.0f,
sz * EobjGetW(eo), sz * EobjGetH(eo), rot_x, rot_y);
#if 1
if (k == sel_ewin)
{
glColor3f(1., 0., 0.);
glRectf(x, y, x + w, y + h);
#define X0 x
#define Y0 y
#define W w
#define H h
glLineWidth(2.);
glColor3f(0., 1., 0.);
glBegin(GL_LINE_LOOP);
glVertex3f(X0, Y0, 0);
glVertex3f(X0 + W, Y0, 0);
glVertex3f(X0 + W, Y0 + H, 0);
glVertex3f(X0, Y0 + H, 0);
glEnd();
glColor4f(1., 1., 1., 1.);
}
#endif
k++;
}
}
}
static void
SceneDraw1(unsigned int tms, EWin **ewins, int num)
{
double t1, arg;
int i;
GLfloat w, h, dx, dy, sz;
EObj *eo;
w = EobjGetW(GLWin.eo);
h = EobjGetH(GLWin.eo);
t1 = 2 * M_PI * (-exp(-(20e-3 * (tms - tn))) / num);
DrawBackground(texture[sel_bg], w, h);
for (i = 0; i < num; i++)
{
arg = t1 + M_PI / 2. - (i - sel_ewin) * 2. * M_PI / num;
dx = (.5 + .3 * cos(arg)) * w;
dy = (.5 - .3 * sin(arg)) * h;
eo = EoObj(ewins[i]);
if (i == sel_ewin)
sz = 0.5 + .01 * cos(10e-3 * (tms - tn));
else
sz = 0.3;
DrawQube(EobjGetTexture(eo), dx, dy, 500.0,
sz * EobjGetW(eo), sz * EobjGetH(eo), rot_x, rot_y);
}
}
static EWin **
GlwinEwins(int *pnum)
{
int i, j, num;
EWin *const *ewins;
EWin **lst, *ewin;
ewins = EwinListGetAll(&num);
lst = EMALLOC(EWin *, num);
for (i = j = 0; i < num; i++)
{
ewin = ewins[i];
if (!EoIsShown(ewin))
continue;
if (ewin->props.skip_focuslist || ewin->props.skip_ext_task)
continue;
lst[j++] = ewin;
}
*pnum = j;
return lst;
}
static void
SceneDraw(void)
{
unsigned int t;
EWin **ewins;
int num;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
t = GetDTime();
ewins = GlwinEwins(&num);
if (num <= 0)
goto done;
if (sel_ewin < 0)
sel_ewin = num - 1;
else if (sel_ewin >= num)
sel_ewin = 0;
GLWin.ewin = ewins[sel_ewin];
switch (Conf_glwin.mode)
{
default:
SceneDraw1(t, ewins, num);
break;
case 1:
SceneDraw2(t, ewins, num);
break;
}
glXSwapBuffers(disp, EobjGetXwin(GLWin.eo));
done:
Efree(ewins);
rot_x += speed_x;
rot_y += speed_y;
}
static int
GlwinRun(EObj *eobj __UNUSED__, int remaining __UNUSED__,
void *state __UNUSED__)
{
if (!GLWin.eo)
return 0;
SceneDraw();
return 1;
}
static int
GlwinKeyPress(GLWindow *gw, EX_KeySym keysym)
{
switch (keysym)
{
case XK_q:
case XK_Escape:
return 1;
case XK_b:
sel_bg += 1;
if (sel_bg >= N_TEXTURES)
sel_bg = 0;
break;
case XK_f:
filter += 1;
if (filter >= 2)
filter = 0;
break;
case XK_g: /* Toggle grabs */
if (gw->grabbing)
{
GrabPointerRelease();
GrabKeyboardRelease();
gw->grabbing = 0;
}
else
{
GrabPointerSet(EobjGetWin(gw->eo), ECSR_GRAB, 0);
GrabKeyboardSet(EobjGetWin(gw->eo));
gw->grabbing = 1;
}
break;
case XK_l:
light = !light;
if (!light)
glDisable(GL_LIGHTING);
else
glEnable(GL_LIGHTING);
break;
case XK_R: /* Reset */
rot_x = rot_y = 0.0f;
/* FALLTHROUGH */
case XK_S: /* Stop */
bg_z = -2.0f;
speed_x = 0.0f;
speed_y = 0.0f;
break;
case XK_Page_Up:
bg_z -= 0.10f;
break;
case XK_Page_Down:
bg_z += 0.10f;
break;
case XK_Up:
speed_x -= 0.10f;
break;
case XK_Down:
speed_x += 0.10f;
break;
case XK_Right:
speed_y += 0.10f;
break;
case XK_Left:
speed_y -= 0.10f;
break;
case XK_m:
Conf_glwin.mode++;
Conf_glwin.mode %= 2;
break;
case XK_n:
case XK_Tab:
sel_ewin++;
tn = GetDTime();
break;
case XK_p:
sel_ewin--;
break;
case XK_Return:
if (!gw->ewin)
{
Eprintf("No win\n");
break;
}
EwinOpActivate(gw->ewin, OPSRC_USER, Conf.warplist.raise_on_select);
return 1;
}
#define TI(no) ((texture[no]) ? (int)texture[no]->texture : -1)
Dprintf("bg=%d(%d) filter=%d l=%d z=%.2f spx/y=%.2f/%.2f\n",
sel_bg, TI(sel_bg), filter, light, bg_z, speed_x, speed_y);
return 0;
}
static void
GlwinEvent(Win win __UNUSED__, XEvent *ev, void *prm)
{
GLWindow *gw = (GLWindow *) prm;
EX_KeySym keysym;
int done = 0;
switch (ev->type)
{
default:
break;
case EX_EVENT_DAMAGE_NOTIFY:
return;
}
Dprintf("GlwinEvent ev %d\n", ev->type);
switch (ev->type)
{
default:
break;
case KeyPress:
keysym = XLookupKeysym(&ev->xkey, ev->xkey.state);
done = GlwinKeyPress(gw, keysym);
break;
#if 0
case EnterNotify:
GrabKeyboardSet(EobjGetWin(GLWin.eo));
break;
case LeaveNotify:
GrabKeyboardRelease();
break;
#endif
case MapNotify:
GlwinKeyPress(gw, XK_g);
AnimatorAdd(GLWin.eo, ANIM_GLWIN, GlwinRun, -1, 0, 0, NULL);
break;
#if 0
case ConfigureNotify:
if (ev->xconfigure.width == EobjGetW(GLWin.eo) &&
ev->xconfigure.height == EobjGetH(GLWin.eo))
break;
SceneResize(ev->xconfigure.width, ev->xconfigure.height);
break;
#endif
}
if (done)
GlwinExit();
}
static int
GlwinCreate(const char *title __UNUSED__, int width, int height)
{
Win win;
int x, y;
#if 0
win = RROOT;
#else
win = VROOT;
#endif
x = ((win->w - width) / 2);
y = ((win->h - height) / 2);
GLWin.eo = EobjWindowCreate(EOBJ_TYPE_GLX, x, y, width, height, 0, "GLwin");
if (!GLWin.eo)
return -1;
win = EobjGetWin(GLWin.eo);
GLWin.eo->fade = GLWin.eo->shadow = 1;
EventCallbackRegister(win, GlwinEvent, &GLWin);
ESelectInput(win, ExposureMask | KeyPressMask | ButtonPressMask |
StructureNotifyMask);
EGlWindowConnect(WinGetXwin(win));
GLWin.grabbing = 0;
GLWin.ewin = NULL;
EobjMap(GLWin.eo, 1);
t0 = GetTimeMs();
tn = t0;
return 0;
}
static void
GlwinInit(void)
{
bg_z = -2.0f;
sel_bg = 0;
filter = 0;
sel_ewin = 0;
light = 0;
if (GlwinCreate("GLwin", 640, 480))
{
Eprintf("Failed to create window\n");
return;
}
Mode_glwin.active = 1;
TexturesLoad();
SceneResize(EobjGetW(GLWin.eo), EobjGetW(GLWin.eo));
SceneInitLight();
glFlush();
}
static void
GlwinExit(void)
{
if (!Mode_glwin.active)
return;
Dprintf("GlTestExit\n");
if (GLWin.eo)
{
EventCallbackUnregister(EobjGetWin(GLWin.eo), GlwinEvent, &GLWin);
EobjWindowDestroy(GLWin.eo);
GLWin.eo = NULL;
}
#if 0
unsigned int i;
for (i = 0; i < N_TEXTURES; i++)
{
EGlTextureDestroy(texture[i]);
texture[i] = NULL;
}
#endif
Mode_glwin.active = 0;
}
/*
* GLwin Module
*/
static void
GlwinIpc(const char *params)
{
const char *cmd;
cmd = params;
if (!cmd)
{
GlwinInit();
}
}
static const IpcItem GlwinIpcArray[] = {
{
GlwinIpc,
"glwin", NULL,
"Glwin functions",
" glwin\n" }
,
};
static const CfgItem GlwinCfgItems[] = {
CFG_ITEM_INT(Conf_glwin, mode, 0),
};
/*
* Module descriptor
*/
extern const EModule ModGlwin;
const EModule ModGlwin = {
"glwin", NULL,
NULL,
MOD_ITEMS(GlwinIpcArray),
MOD_ITEMS(GlwinCfgItems)
};