Скорее просто небольшой хак, порожденный моим воспаленным разумом. Работает только под X11, так что виндузятникам не грозит. Фактически трюк заключается в получении от емакса id окна, передачи его сишной программулине, которая создает ему подокно с OpenGL контекстом.
Обязательный скриншот ниже:
Ну, а теперь код. Начнем с сишного кода. Это такой небольшой полиглот, так что для сборки достаточно сделать его исполняемым и запустить.
#if 0
gcc -o ./emacsgl $0 -lX11 -lGL -l GLU
exit
#endif
/* Xl-ib.h is the default header that is included and has the core functionallity */
#include
/* Xatom.h includes functionallity for creating new protocol messages */
#include
/* keysym.h contains keysymbols which we use to resolv what keys that are being pressed */
#include
/* printf */
#include
/* the XF86 Video Mode extension allows us to change the displaymode of the server
* this allows us to set the display to fullscreen and also read videomodes and
* other information.
*/
#include
/* gl.h we need OpenGL :-) */
#include
/* this file is needed for X11 applications if we want to use hardware rendering */
#include
#include
#define WIDTH 640
#define HEIGHT 480
#define TITLE "EmacsGL"
typedef struct {
Display *dpy;
int screen;
Window win;
GLXContext ctx;
XSetWindowAttributes attr;
Bool fs;
Bool doubleBuffered;
XF86VidModeModeInfo deskMode;
int x, y;
unsigned int width, height;
unsigned int depth;
} GLWindow;
GLWindow GLWin;
/* most important variable
* it contains information about the X server which we communicate with
*/
Display * display;
int screen;
/* our window instance */
Window window;
GLXContext context;
XSetWindowAttributes winAttr;
XWindowAttributes emacsAttr;
Window emacsWindowId;
Bool fullscreen = False;
Bool doubleBuffered;
/* original desktop mode which we save so we can restore it later */
XF86VidModeModeInfo desktopMode;
int x, y;
unsigned int width, height;
unsigned int depth;
GLfloat rotQuad = 0.0f;
/* attributes for a single buffered visual in RGBA format with at least
* 4 bits per color and a 16 bit depth buffer */
static int attrListSgl[] =
{
GLX_RGBA, GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
None
};
/* attributes for a double buffered visual in RGBA format with at least
* 4 bits per color and a 16 bit depth buffer */
static int attrListDbl[] =
{
GLX_RGBA, GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
None
};
/* prototypes */
void createWindow();
void destroyWindow();
void resizeGL(unsigned int, unsigned int);
void initGL();
/*
* create a window
*/
void createWindow()
{
XVisualInfo *vi;
Colormap cmap;
int i, dpyWidth, dpyHeight;
int glxMajor, glxMinor, vmMajor, vmMinor;
XF86VidModeModeInfo **modes;
int modeNum, bestMode;
Atom wmDelete;
Window winDummy;
unsigned int borderDummy;
/* set best mode to current */
bestMode = 0;
/* get a connection */
display = XOpenDisplay(0);
screen = DefaultScreen(display);
XF86VidModeQueryVersion(display, &vmMajor, &vmMinor);
printf("XF86 VideoMode extension version %d.%d\n", vmMajor, vmMinor);
XF86VidModeGetAllModeLines(display, screen, &modeNum, &modes);
/* save desktop-resolution before switching modes */
GLWin.deskMode = *modes[0];
desktopMode = *modes[0];
/* look for mode with requested resolution */
for (i = 0; i < modeNum; i++)
{
if ((modes[i]->hdisplay == width) && (modes[i]->vdisplay == height))
bestMode = i;
}
/* get an appropriate visual */
vi = glXChooseVisual(display, screen, attrListDbl);
if (vi == NULL)
{
vi = glXChooseVisual(display, screen, attrListSgl);
doubleBuffered = False;
printf("singlebuffered rendering will be used, no doublebuffering available\n");
}
else
{
doubleBuffered = True;
printf("doublebuffered rendering available\n");
}
glXQueryVersion(display, &glxMajor, &glxMinor);
printf("GLX-Version %d.%d\n", glxMajor, glxMinor);
/* create a GLX context */
context = glXCreateContext(display, vi, 0, GL_TRUE);
/* create a color map */
cmap = XCreateColormap(display, RootWindow(display, vi->screen),
vi->visual, AllocNone);
winAttr.colormap = cmap;
winAttr.border_pixel = 0;
/* create a window in window mode*/
winAttr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask |
StructureNotifyMask;
XGetWindowAttributes( display, emacsWindowId, &emacsAttr );
window = XCreateWindow(display, emacsWindowId,
0, 0, emacsAttr.width, emacsAttr.height, 0, vi->depth, InputOutput, vi->visual,
CWBorderPixel | CWColormap | CWEventMask, &winAttr);
/* only set window title and handle wm_delete_events if in windowed mode */
wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(display, window, &wmDelete, 1);
XSetStandardProperties(display, window, TITLE,
TITLE, None, NULL, 0, NULL);
XMapRaised(display, window);
/* connect the glx-context to the window */
glXMakeCurrent(display, window, context);
if (glXIsDirect(display, context))
printf("DRI enabled\n");
else
printf("no DRI available\n");
initGL();
}
/*
* destroy the window
*/
void destroyWindow()
{
if( context )
{
if( !glXMakeCurrent(display, None, NULL))
{
printf("Could not release drawing context.\n");
}
/* destroy the context */
glXDestroyContext(display, context);
context = NULL;
}
/* switch back to original desktop resolution if we were in fullscreen */
if( fullscreen )
{
XF86VidModeSwitchToMode(display, screen, &desktopMode);
XF86VidModeSetViewPort(display, screen, 0, 0);
}
XCloseDisplay(display);
}
void resizeGL(unsigned int width, unsigned int height)
{
/* prevent divide-by-zero */
if (height == 0)
height = 1;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
}
void initGL()
{
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
/* we use resizeGL once to set up our initial perspective */
resizeGL(width, height);
/* Reset the rotation angle of our object */
rotQuad = 0;
glFlush();
}
void renderGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -7.0f);
glRotatef(rotQuad, 1.0f, 0.5f, 0.25f);
glBegin(GL_QUADS);
/* top of cube */
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
/* bottom of cube */
glColor3f(1.0f, 0.5f, 0.0f);
glVertex3f(1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
/* front of cube */
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f);
/* back of cube */
glColor3f(1.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f(1.0f, 1.0f, -1.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
/* right side of cube */
glColor3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, -1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
/* left side of cube */
glColor3f(0.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glEnd();
rotQuad += 0.1f;
/* swap the buffers if we have doublebuffered */
if (doubleBuffered)
{
glXSwapBuffers(display, window);
}
}
int main(int argc, char ** argv)
{
XEvent event;
Bool done = False;
if (argc < 2)
{
printf ("Necromant's emacsGL proof-of-concept\n");
printf ("Usage:\t %s [window-id]\n", argv[0]);
printf ("Window is must be obtained from within emacs, lol. \n");
printf ("This code is subject to WTFPL, google for it\n");
exit(1);
}
width = WIDTH;
height = HEIGHT;
sscanf(argv[1],"%d",&emacsWindowId);
printf("Emacs WinID: %d\n",emacsWindowId);
createWindow();
/* wait for events and eat up cpu. ;-) */
while (!done)
{
/* handle the events in the queue */
while (XPending(display) > 0)
{
XNextEvent(display, &event);
switch (event.type)
{
case Expose:
if (event.xexpose.count != 0)
break;
renderGL();
break;
case ConfigureNotify:
/* call resizeGL only if our window-size changed */
if ((event.xconfigure.width != GLWin.width) ||
(event.xconfigure.height != GLWin.height))
{
width = event.xconfigure.width;
height = event.xconfigure.height;
resizeGL(width, height);
}
break;
/* exit in case of a mouse button press */
case ButtonPress:
done = True;
break;
case KeyPress:
if (XLookupKeysym(&event.xkey, 0) == XK_Escape)
{
done = True;
}
if (XLookupKeysym(&event.xkey,0) == XK_F1)
{
destroyWindow();
fullscreen = !fullscreen;
createWindow();
}
break;
case ClientMessage:
if (strcmp(XGetAtomName(display, event.xclient.message_type),
"WM_PROTOCOLS") == 0)
{
done = True;
}
break;
default:
break;
}
}
renderGL();
}
destroyWindow();
return 0;
}
Сохраняем куда-нибудь, или wget’им с моего пастебина:
wget -o emacsgl.c http://p.ncrmnt.org/1013/raw/
chmod +x emacsgl.c
./emacsgl.c
Это нам соберет бинарь emacsgl. Запоминаем путь к нему, и втыкаем в этот сниплет на emacs-lisp’e.
(shell-command
(concat "/full/path/to/emacsgl "
(cdr (assoc 'window-id (frame-parameters (selected-frame))))))
Копипастим в скретч, давим C-j, наслаждаемся.

Leave a Reply